dedsudiyu 2 napja
szülő
commit
07fdb4110e
24 módosított fájl, 976 hozzáadás és 203 törlés
  1. 16 0
      src/api/safetyEducationExaminationNew/index.js
  2. BIN
      src/assets/ZDimages/basicsModules/image_420782143223404.png
  3. BIN
      src/assets/ZDimages/basicsModules/image_986222064626770.png
  4. 213 0
      src/views/safetyEducationExaminationNew/components/richTextPdf.vue
  5. 6 6
      src/views/safetyEducationExaminationNew/courseManagement/courseManagement/basicSettings.vue
  6. 14 14
      src/views/safetyEducationExaminationNew/courseManagement/coursewareManagement/addPage.vue
  7. 6 2
      src/views/safetyEducationExaminationNew/workbench/ExamSituation.vue
  8. 6 2
      src/views/safetyEducationExaminationNew/workbench/LabPersonMatch.vue
  9. 6 2
      src/views/safetyEducationExaminationNew/workbench/StudyHourStats.vue
  10. 55 0
      src/views/safetyEducationExaminationNew/dataStatistics/index.vue
  11. 27 6
      src/views/safetyEducationExaminationNew/examManagement/examinationArrange/addPage.vue
  12. 3 1
      src/views/safetyEducationExaminationNew/examManagement/examinationArrange/index.vue
  13. 2 2
      src/views/safetyEducationExaminationNew/examManagement/testPaperManagement/addPage.vue
  14. 5 7
      src/views/safetyEducationExaminationNew/workbench/LabBindingApply.vue
  15. 314 0
      src/views/safetyEducationExaminationNew/personnelApproval/index.vue
  16. 1 1
      src/views/safetyEducationExaminationNew/questionBankManagement/practiceSolvingProblems/index.vue
  17. 32 9
      src/views/safetyEducationExaminationNew/questionBankManagement/questionBankManagement/addPage.vue
  18. 1 1
      src/views/safetyEducationExaminationNew/questionBankManagement/questionBankManagement/index.vue
  19. 4 3
      src/views/safetyEducationExaminationNew/safetyEducation/knowledgeLearning/index.vue
  20. 98 27
      src/views/safetyEducationExaminationNew/safetyTest/onlineExam/index.vue
  21. 15 6
      src/views/safetyEducationExaminationNew/safetyTest/practiceQuestions/index.vue
  22. 25 38
      src/views/safetyEducationExaminationNew/workbench/ResourceCard.vue
  23. 124 56
      src/views/safetyEducationExaminationNew/workbench/TaskCard.vue
  24. 3 20
      src/views/safetyEducationExaminationNew/workbench/index.vue

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

@@ -868,3 +868,19 @@ export function examUserExamStudyTaskList(data) {
     data: data
   })
 }
+//工作台按知识点分组查询试题数量
+export function examElQuestionBankGroupByKnowledgePoint(data) {
+  return request({
+    url: '/exam/elQuestionBank/groupByKnowledgePoint',
+    method: 'post',
+    data: data
+  })
+}
+//工作台按知识点分组查询试题数量
+export function examUserPracticeSummary(query) {
+  return request({
+    url: '/exam/user/practice/summary',
+    method: 'get',
+    params: query
+  })
+}

BIN
src/assets/ZDimages/basicsModules/image_420782143223404.png


BIN
src/assets/ZDimages/basicsModules/image_986222064626770.png


+ 213 - 0
src/views/safetyEducationExaminationNew/components/richTextPdf.vue

@@ -0,0 +1,213 @@
+<template>
+  <div class="rich-text-pdf-wrapper">
+    <div ref="a4Paper" class="a4-paper">
+      <div class="rich-content" v-html="content"></div>
+    </div>
+  </div>
+</template>
+
+<script>
+  import html2canvas from 'html2canvas';
+
+  export default {
+    name: 'RichTextPdf',
+    props: {
+      content: {
+        type: String,
+        default: ''
+      }
+    },
+    data() {
+      return {
+        loading: false
+      };
+    },
+    methods: {
+      async downloadPDF() {
+        if (!this.content) {
+          alert('没有可下载的内容');
+          return;
+        }
+
+        this.loading = true;
+        try {
+          const el = this.$refs.a4Paper;
+          // 将 DOM 转换为 Canvas,scale: 2 保证高清输出
+          const canvas = await html2canvas(el, {
+            scale: 2,
+            useCORS: true, // 允许跨域图片
+            backgroundColor: '#ffffff',
+            logging: false
+          });
+
+          const imgW = canvas.width;
+          const imgH = canvas.height;
+
+          // A4 纸的宽度点数 (595 pt = 210mm)
+          const a4WidthPt = 595;
+          // 根据原图比例计算 A4 纸应有的高度点数
+          const a4HeightPt = Math.round((imgH / imgW) * a4WidthPt);
+
+          // A4 纸标准高度点数 (842 pt = 297mm)
+          const pageHeightPt = 842;
+
+          // 计算需要多少页
+          const totalPages = Math.ceil(a4HeightPt / pageHeightPt);
+
+          // 将 canvas 转为 JPEG base64 并解码为二进制字节数组
+          const jpegB64 = canvas.toDataURL('image/jpeg', 0.92).split(',')[1];
+          const jpegBytes = Uint8Array.from(atob(jpegB64), c => c.charCodeAt(0));
+
+          // 调用你的底层 PDF 构建方法(已改造为支持多页)
+          const pdfBytes = this._buildMultiPagePdf(jpegBytes, imgW, imgH, a4WidthPt, a4HeightPt, pageHeightPt, totalPages);
+
+          // 触发浏览器下载
+          const blob = new Blob([pdfBytes], { type: 'application/pdf' });
+          const url = URL.createObjectURL(blob);
+          const a = document.createElement('a');
+          a.href = url;
+          a.download = '富文本内容.pdf';
+          document.body.appendChild(a);
+          a.click();
+          URL.revokeObjectURL(url);
+          document.body.removeChild(a);
+
+        } catch (error) {
+          console.error('PDF 生成失败:', error);
+          alert('PDF 下载失败,请重试!');
+        } finally {
+          this.loading = false;
+        }
+      },
+
+      // 核心:构造内嵌 JPEG 图片的多页最小 PDF
+      _buildMultiPagePdf(jpegBytes, imgW, imgH, pageW, totalImgH, pageH, pageCount) {
+        const enc = s => new TextEncoder().encode(s);
+        const concat = (...parts) => {
+          const total = parts.reduce((n, p) => n + p.length, 0);
+          const out = new Uint8Array(total);
+          let off = 0;
+          for (const p of parts) { out.set(p, off); off += p.length; }
+          return out;
+        };
+
+        // 动态生成每一页的 Page 对象和 Contents 对象
+        const pageObjects = [];
+        const contentObjects = [];
+
+        for (let i = 0; i < pageCount; i++) {
+          const contentId = 3 + i * 2;      // Contents 对象 ID (从 3 开始)
+          const pageId = 4 + i * 2;         // Page 对象 ID (从 4 开始)
+
+          // 计算当前页的裁剪偏移量 (Y轴向下移动)
+          const yOffset = -(i * pageH);
+          // 注意:这里使用 /Do 绘制整图,但通过 cm 变换来实现视觉上的“分页裁剪”效果
+          const stream = `q ${pageW} 0 0 ${totalImgH} 0 ${yOffset} cm /Im0 Do Q\n`;
+
+          const streamEnc = enc(stream);
+          contentObjects.push({
+            id: contentId,
+            bytes: concat(
+              enc(`${contentId} 0 obj\n<< /Length ${streamEnc.length} >>\nstream\n`),
+              streamEnc,
+              enc('\nendstream\nendobj\n')
+            )
+          });
+
+          pageObjects.push({
+            id: pageId,
+            bytes: concat(
+              enc(`${pageId} 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${pageW} ${pageH}] /Contents ${contentId} 0 R /Resources << /XObject << /Im0 5 0 R >> >> >>\nendobj\n`)
+            )
+          });
+        }
+
+        // 构建 Kids 数组字符串
+        const kidsStr = pageObjects.map(p => `${p.id} 0 R`).join(' ');
+
+        // 基础对象
+        const o1 = { id: 1, bytes: concat(enc('1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n')) };
+        const o2 = { id: 2, bytes: concat(enc(`2 0 obj\n<< /Type /Pages /Kids [${kidsStr}] /Count ${pageCount} >>\nendobj\n`)) };
+
+        // 图片对象 (所有页共享同一张长图)
+        const o5 = {
+          id: 5,
+          bytes: concat(
+            enc(`5 0 obj\n<< /Type /XObject /Subtype /Image /Width ${imgW} /Height ${imgH} /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length ${jpegBytes.length} >>\nstream\n`),
+            jpegBytes,
+            enc('\nendstream\nendobj\n')
+          )
+        };
+
+        // 组装所有对象
+        const header = enc('%PDF-1.4\n');
+        const allObjs = [o1, o2, ...contentObjects, ...pageObjects, o5];
+
+        const offsets = [];
+        let pos = header.length;
+        const bodyParts = [];
+
+        for (const o of allObjs) {
+          offsets.push(pos);
+          pos += o.bytes.length;
+          bodyParts.push(o.bytes);
+        }
+
+        // 构建交叉引用表 (xref)
+        const totalObjsCount = allObjs.length + 1; // 包含 0 号对象
+        let xrefStr = `xref\n0 ${totalObjsCount}\n0000000000 65535 f \n`;
+        for (const offset of offsets) {
+          xrefStr += `${String(offset).padStart(10, '0')} 00000 n \n`;
+        }
+        xrefStr += `trailer\n<< /Size ${totalObjsCount} /Root 1 0 R >>\nstartxref\n${pos}\n%%EOF\n`;
+
+        const xref = enc(xrefStr);
+        return concat(header, ...bodyParts, xref);
+      }
+    }
+  };
+</script>
+
+<style scoped>
+  .rich-text-pdf-wrapper {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+
+  /* 核心:严格限制容器为 A4 纸比例 (210mm x 297mm ≈ 794px x 1123px) */
+  .a4-paper {
+    width: 794px;
+    min-height: 1123px;
+    padding: 40px;
+    box-sizing: border-box;
+    background: #fff;
+    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
+    overflow: hidden;
+    position: relative;
+  }
+
+  /* 重置并规范富文本内部样式 */
+  .rich-content {
+    font-family: "SimSun", "宋体", serif;
+    font-size: 16px;
+    line-height: 1.8;
+    color: #333;
+    word-wrap: break-word;
+  }
+
+  .rich-content p { margin: 8px 0; }
+
+  .action-bar { margin-top: 20px; }
+
+  .download-btn {
+    padding: 10px 24px;
+    background: #409eff;
+    color: #fff;
+    border: none;
+    border-radius: 4px;
+    font-size: 16px;
+    cursor: pointer;
+  }
+  .download-btn:disabled { background: #a0cfff; cursor: not-allowed; }
+</style>

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

@@ -5,8 +5,8 @@
         <div class="form-big-box">
           <div class="form-box">
             <div class="form-flex-box">
-              <el-form-item label="专业类别" prop="disciplineTypeId">
-                <el-select v-model="newData.disciplineTypeId" :disabled="showType" placeholder="请选择专业类别" style="width: 160px">
+              <el-form-item label="课程分类" prop="disciplineTypeId">
+                <el-select v-model="newData.disciplineTypeId" :disabled="showType" placeholder="请选择课程分类" style="width: 160px">
                   <el-option
                     v-for="dict in optionListA"
                     :key="dict.id"
@@ -133,10 +133,10 @@
           popupIntervalMinutes: 0,
         },
         rules:{
-          disciplineTypeId: [
-            { required: true, message: "请选择专业类别", trigger: "blur" },
-            { required: true, message: "请选择专业类别", validator: this.spaceJudgment, trigger: "blur" }
-          ],
+          // disciplineTypeId: [
+          //   { required: true, message: "请选择课程分类", trigger: "blur" },
+          //   { required: true, message: "请选择课程分类", validator: this.spaceJudgment, trigger: "blur" }
+          // ],
           courseName: [
             { required: true, message: "请输入课程名称", trigger: "blur" },
             { required: true, message: "请输入课程名称", validator: this.spaceJudgment, trigger: "blur" }

+ 14 - 14
src/views/safetyEducationExaminationNew/courseManagement/coursewareManagement/addPage.vue

@@ -53,16 +53,16 @@
                 </el-switch>
                 <span style="margin-left:15px;">{{newData.shareStatus==1?'共享开启,全校所有学院单位都可见':'不共享,仅本学院单位及上级都可见'}}</span>
               </el-form-item>
-              <!--<el-form-item label="专业类别" prop="disciplineTypeId">-->
-                <!--<el-select v-model="newData.disciplineTypeId" placeholder="请选择专业类别" style="width: 400px">-->
-                  <!--<el-option-->
-                    <!--v-for="dict in optionListC"-->
-                    <!--:key="dict.id"-->
-                    <!--:label="dict.disciplineName"-->
-                    <!--:value="dict.id"-->
-                  <!--/>-->
-                <!--</el-select>-->
-              <!--</el-form-item>-->
+              <el-form-item label="课件分类" prop="disciplineTypeId">
+                <el-select v-model="newData.disciplineTypeId" placeholder="请选择课件分类" style="width: 400px">
+                  <el-option
+                    v-for="dict in optionListC"
+                    :key="dict.id"
+                    :label="dict.disciplineName"
+                    :value="dict.id"
+                  />
+                </el-select>
+              </el-form-item>
             </div>
           </div>
           <div class="form-box" v-if="newData.materialType == 1">
@@ -293,8 +293,8 @@
             { required: true, message: "请选择知识点", validator: this.spaceJudgment, trigger: "blur" }
           ],
           disciplineTypeId: [
-            { required: true, message: "请选择专业类别", trigger: "blur" },
-            { required: true, message: "请选择专业类别", validator: this.spaceJudgment, trigger: "blur" }
+            { required: true, message: "请选择课件分类", trigger: "blur" },
+            { required: true, message: "请选择课件分类", validator: this.spaceJudgment, trigger: "blur" }
           ],
           attachments: [
             { required: true, message: "请上传课件", trigger: "blur" },
@@ -365,7 +365,7 @@
     },
     mounted(){
       this.examElKnowledgePointTreeList();
-      // this.examElDisciplineTypeList();
+      this.examElDisciplineTypeList();
     },
     methods:{
       getData(){
@@ -375,7 +375,7 @@
               id:response.data.id,
               materialType:response.data.materialType,
               knowledgePointId:response.data.knowledgePointId,
-              disciplineTypeId:'',
+              disciplineTypeId:response.data.disciplineTypeId,
               watermark:response.data.watermark?response.data.watermark:'',
               coursewareName:response.data.coursewareName,
               minLearningDuration:response.data.minLearningDuration,

+ 6 - 2
src/views/safetyEducationExaminationNew/workbench/ExamSituation.vue

@@ -384,10 +384,14 @@ export default {
 <style scoped lang="scss">
 .exam-situation-card {
   background: #fff;
-  border-radius: 6px;
+  /*border-radius: 6px;*/
   padding: 20px;
   margin-bottom: 12px;
-  box-shadow: 0 1px 4px rgba(0,0,0,0.06);
+  /*box-shadow: 0 1px 4px rgba(0,0,0,0.06);*/
+  background: rgba(255, 255, 255, 0.95);
+  border: 1px solid #e8edf5;
+  border-radius: 16px;
+  box-shadow: 0 4px 20px rgba(37, 50, 74, 0.06);
 
   .card-header {
     display: flex;

+ 6 - 2
src/views/safetyEducationExaminationNew/workbench/LabPersonMatch.vue

@@ -213,10 +213,14 @@ export default {
 <style scoped lang="scss">
 .lab-person-card {
   background: #fff;
-  border-radius: 6px;
+  /*border-radius: 6px;*/
   padding: 20px;
   margin-bottom: 12px;
-  box-shadow: 0 1px 4px rgba(0,0,0,0.06);
+  /*box-shadow: 0 1px 4px rgba(0,0,0,0.06);*/
+  background: rgba(255, 255, 255, 0.95);
+  border: 1px solid #e8edf5;
+  border-radius: 16px;
+  box-shadow: 0 4px 20px rgba(37, 50, 74, 0.06);
 
   .card-header {
     display: flex;

+ 6 - 2
src/views/safetyEducationExaminationNew/workbench/StudyHourStats.vue

@@ -263,10 +263,14 @@ export default {
 <style scoped lang="scss">
 .study-hour-card {
   background: #fff;
-  border-radius: 6px;
+  /*border-radius: 6px;*/
   padding: 16px 20px;
   margin-bottom: 12px;
-  box-shadow: 0 1px 4px rgba(0,0,0,0.06);
+  /*box-shadow: 0 1px 4px rgba(0,0,0,0.06);*/
+  background: rgba(255, 255, 255, 0.95);
+  border: 1px solid #e8edf5;
+  border-radius: 16px;
+  box-shadow: 0 4px 20px rgba(37, 50, 74, 0.06);
 
   .card-header {
     display: flex;

+ 55 - 0
src/views/safetyEducationExaminationNew/dataStatistics/index.vue

@@ -0,0 +1,55 @@
+<!-- 数据统计 -->
+<template>
+  <div class="app-container dataStatistics">
+    <div class="page-container dataStatisticsPage scrollbar-box">
+      <!-- 考试情况统计 -->
+      <exam-situation/>
+      <!-- 实验室与人员匹配 -->
+      <lab-person-match/>
+      <!-- 学时统计 -->
+      <study-hour-stats/>
+    </div>
+  </div>
+</template>
+<script>
+  import ExamSituation from './ExamSituation.vue'
+  import LabPersonMatch from './LabPersonMatch.vue'
+  import StudyHourStats from './StudyHourStats.vue'
+  export default {
+    name: 'index',
+    components: {
+      ExamSituation,
+      LabPersonMatch,
+      StudyHourStats,
+    },
+    data() {
+      return {
+
+      }
+    },
+    created() {
+
+    },
+    mounted() {
+
+    },
+    methods: {
+
+    }
+  }
+</script>
+<style scoped lang="scss">
+  .dataStatistics {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    overflow: hidden;
+    .dataStatisticsPage {
+      padding: 16px;
+      background: #f5f7fa;
+      overflow-y: scroll !important;
+      display: flex;
+      flex-direction: column;
+    }
+  }
+</style>

+ 27 - 6
src/views/safetyEducationExaminationNew/examManagement/examinationArrange/addPage.vue

@@ -30,15 +30,20 @@
                   <el-form-item label="考试类型" prop="examTypeId">
                     <el-select :disabled="showType" v-model="newData.examTypeId" placeholder="考试类型" @change="examChange" style="width:200px;">
                       <el-option v-for="item in optionExamType" :key="item.id" :label="item.examTypeName"
-                                 :value="item.id"
-                      />
+                                 :value="item.id"/>
+                    </el-select>
+                  </el-form-item>
+                  <el-form-item label="安全分类" :prop="placementTest?'disciplineTypeId':''">
+                    <el-select :disabled="showType" v-model="newData.disciplineTypeId" placeholder="安全分类" @change="examChange" style="width:200px;">
+                      <el-option v-for="item in tableList" :key="item.id"
+                                 :label="item.disciplineName"
+                                 :value="item.id"/>
                     </el-select>
                   </el-form-item>
                   <el-form-item label="风险等级" prop="riskLevelId" v-if="placementTest">
                     <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"
-                      />
+                                 :value="item.levelId"/>
                     </el-select>
                   </el-form-item>
                 </div>
@@ -581,7 +586,7 @@
   import selectScope from './selectScope.vue'
   import selectCertificate from './selectCertificate.vue'
   import {
-    examElExamAdd,examElExamEdit,examElExamDetail,examElKnowledgePointTreeList,
+    examElExamAdd,examElExamEdit,examElExamDetail,examElKnowledgePointTreeList,examElDisciplineTypeList,
     examElExamTypeList, laboratoryLevelConfigGetLevelTitleList,examElConfigMasterInitMinHour
   } from '@/api/safetyEducationExaminationNew/index'
 
@@ -713,6 +718,7 @@
         ],
         optionListC:[],
         optionExamType: [],
+        tableList: [],
         optionRiskLevel: [],
         scopeList: [],
         userList: [],
@@ -731,6 +737,7 @@
         },
         newData: {
           examTypeId: null,
+          disciplineTypeId: null,
           riskLevelId: null,
           examName: '',
           attemptLimit: 1,
@@ -779,6 +786,9 @@
           examTypeId: [
             { required: true, message: '请选择考试类型', trigger: 'change' }
           ],
+          disciplineTypeId: [
+            { required: true, message: '请选择安全分类', trigger: 'change' }
+          ],
           riskLevelId: [
             { required: true, message: '请选择风险等级', trigger: 'change' }
           ],
@@ -867,6 +877,7 @@
       this.laboratoryLevelConfigGetLevelTitleList();
       this.examElConfigMasterInitMinHour();
       this.examElKnowledgePointTreeList();
+      this.examElDisciplineTypeList();
     },
     methods: {
       initialize() {
@@ -881,6 +892,7 @@
             let obj = {
               id:this.propsData.id,
               examTypeId: response.data.examTypeId,
+              disciplineTypeId: response.data.disciplineTypeId,
               riskLevelId: response.data.riskLevelId,
               examName: response.data.examName,
               attemptLimit: response.data.attemptLimit,
@@ -939,7 +951,7 @@
           if (!valid) {
             const firstErrorField = Object.keys(fields)[0]
             const tabFieldMap = [
-              { tab: 'basic', list: ['examTypeId','riskLevelId','examName','attemptLimit','paperId','enableStatus','answerDuration','scopeType','examTimeRange'] },
+              { tab: 'basic', list: ['examTypeId','disciplineTypeId','riskLevelId','examName','attemptLimit','paperId','enableStatus','answerDuration','scopeType','examTimeRange'] },
               { tab: 'preExam', list: ['studyHoursRequirementEnabled','preCourseEnabled','mockEnabled','commitmentEnabled'] },
               // { tab: 'antiCheat', list: ['popupVerifyEnabled'] },
               { tab: 'certificate', list: ['certificateEnabled','certificateValidMonths','certificateReviewMonths'] }
@@ -963,6 +975,7 @@
         let self = this;
         let obj = {
           examTypeId:this.newData.examTypeId,
+          disciplineTypeId:this.newData.disciplineTypeId,
           riskLevelId:this.placementTest?this.newData.riskLevelId:'',
           examName:this.newData.examName,
           attemptLimit:this.answerUnlimited?0:this.newData.attemptLimit,
@@ -1170,6 +1183,14 @@
           this.$set(this, 'optionListC', list);
         });
       },
+      //类型选项卡数据
+      examElDisciplineTypeList(){
+        examElDisciplineTypeList({}).then(response => {
+          const validCodes = ['CHEMISTRY', 'BIOLOGY', 'RADIATION', 'MECHANICAL_ELECTRICAL', 'OTHER'];
+          let list = response.data.filter(item => validCodes.includes(item.disciplineTypeCode));
+          this.$set(this, 'tableList', list)
+        })
+      },
     }
   }
 </script>

+ 3 - 1
src/views/safetyEducationExaminationNew/examManagement/examinationArrange/index.vue

@@ -100,7 +100,7 @@
             </template>
           </el-table-column>
           <el-table-column label="考试类型" prop="examTypeName" width="140" show-overflow-tooltip/>
-          <el-table-column label="专业类别" prop="disciplineTypeName" width="140" show-overflow-tooltip/>
+          <el-table-column label="安全分类" prop="disciplineTypeName" width="140" show-overflow-tooltip/>
           <el-table-column label="是否共享" prop="shareStatus" width="120" show-overflow-tooltip>
             <template slot-scope="scope">
               <span style="padding:2px 10px;border:1px solid rgb(1, 131, 251);border-radius:4px;color: rgb(1, 131, 251);background-color:rgba(1, 131, 251,0.2)" v-if="scope.row.shareStatus==1">共享</span>
@@ -137,6 +137,7 @@
                    @click="tableButton(2,scope.row)"
                 >补考</p>
                 <p class="table-button-p"
+                   v-if="scope.row.canEditDelete"
                    v-hasPermiRouter="['exam:elExam:edit']"
                    @click="tableButton(3,scope.row)"
                 >编辑</p>
@@ -145,6 +146,7 @@
                    @click="tableButton(4,scope.row)"
                 >复制</p>
                 <p class="table-button-p"
+                   v-if="scope.row.canEditDelete"
                    v-hasPermiRouter="['exam:elExam:del']"
                    @click="tableButton(5,scope.row)"
                 >删除</p>

+ 2 - 2
src/views/safetyEducationExaminationNew/examManagement/testPaperManagement/addPage.vue

@@ -69,8 +69,8 @@
 
           <!-- 专业类别 / 是否共享 -->
           <div class="flex-row-box">
-            <el-form-item label="专业类别" prop="disciplineTypeId">
-              <el-select :disabled="showType" v-model="newData.disciplineTypeId" placeholder="专业类别" style="width:200px;">
+            <el-form-item label="试卷分类" prop="disciplineTypeId">
+              <el-select :disabled="showType" v-model="newData.disciplineTypeId" placeholder="试卷分类" style="width:200px;">
                 <el-option
                   v-for="item in optionListA"
                   :key="item.id"

+ 5 - 7
src/views/safetyEducationExaminationNew/workbench/LabBindingApply.vue

@@ -1,6 +1,6 @@
 <!-- 实验室绑定申请 -->
 <template>
-  <div class="lab-binding-card" v-show="dataList[0]||showType">
+  <div class="app-container lab-binding-card">
     <div class="card-header">
       <span class="card-title">实验室绑定申请</span>
       <div class="header-right">
@@ -121,7 +121,6 @@ export default {
       dataList: [],
       //数据数量
       total: 0,
-      showType:false,
       dialogType:false,
       dialogData:{
         reason:'',
@@ -198,9 +197,6 @@ export default {
       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)
       })
@@ -212,10 +208,12 @@ export default {
 <style scoped lang="scss">
 .lab-binding-card {
   background: #fff;
-  border-radius: 6px;
+  /*border-radius: 6px;*/
   padding: 20px;
   margin-bottom: 12px;
-  box-shadow: 0 1px 4px rgba(0,0,0,0.06);
+  /*box-shadow: 0 1px 4px rgba(0,0,0,0.06);*/
+  background: rgba(255, 255, 255, 0.95);
+  box-shadow: 0 4px 20px rgba(37, 50, 74, 0.06);
 
   .card-header {
     display: flex;

+ 314 - 0
src/views/safetyEducationExaminationNew/personnelApproval/index.vue

@@ -0,0 +1,314 @@
+<!-- 实验室绑定申请 -->
+<template>
+  <div class="app-container lab-binding-card">
+    <div class="page-container">
+      <div class="card-header">
+        <span class="card-title">实验室绑定申请</span>
+        <div class="header-right">
+          <!-- 分组筛选按钮 -->
+          <div class="table-top-button-box">
+            <div>
+              <p
+                v-for="(btn, i) in filterData.statusBtns" :key="btn.value"
+                :class="filterData.activeStatus === btn.value ? 'check-table-button' : ''"
+                @click="checkButton(btn.value)"
+              >{{ btn.label }}</p>
+            </div>
+          </div>
+        </div>
+      </div>
+      <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>
+          </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>
+          </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>
+          </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>
+  </div>
+</template>
+
+<script>
+  import {
+    examElLabEduRelationAuditList,examElLabEduRelationAudit,
+  } from "@/api/safetyEducationExaminationNew/index";
+  export default {
+    name: 'LabBindingApply',
+    data() {
+      return {
+        filterData: {
+          activeStatus: '',
+          statusBtns: [
+            { label: '全部',   value: '' },
+            { label: '待审批', value: '0' },
+            { label: '已通过', value: '1' },
+            { label: '已驳回', value: '2' },
+          ]
+        },
+        queryParams: {
+          page: 1,
+          pageSize: 20,
+        },
+        dataList: [],
+        //数据数量
+        total: 0,
+        dialogType:false,
+        dialogData:{
+          reason:'',
+        },
+        dialogRules:{
+          reason: [
+            { required: true, message: "请输入驳回原因", trigger: "blur" }
+          ],
+        },
+        checkId:null,
+      }
+    },
+    mounted(){
+      this.getList();
+    },
+    methods: {
+      checkButton(val){
+        if(this.filterData.activeStatus != val){
+          this.$set(this.filterData,'activeStatus',val);
+          this.handleQuery();
+        }
+      },
+      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);
+        }
+      },
+      //提交按钮
+      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.$set(this,'dialogType',false);
+          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 => {
+          this.$set(this, 'dataList', response.data.records)
+          this.$set(this, 'total', response.data.total)
+        })
+      },
+    },
+  }
+</script>
+
+<style scoped lang="scss">
+  .lab-binding-card {
+    padding:20px;
+
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      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;
+      }
+    }
+    .page-content-box{
+      height:500px;
+      padding:0;
+    }
+
+    /* 分组筛选按钮 — 与其他页面保持一致 */
+    .table-top-button-box {
+      display: inline-block;
+      vertical-align: top;
+
+      div {
+        display: flex;
+
+        p {
+          border: 1px solid #DCDFE6;
+          width: 80px;
+          text-align: center;
+          height: 40px;
+          line-height: 40px;
+          cursor: pointer;
+          font-size: 14px;
+
+          &:nth-child(1) {
+            border-top-left-radius: 4px;
+            border-bottom-left-radius: 4px;
+          }
+          &:nth-child(2) { border-left: none; }
+          &:nth-child(3) { border-left: none; border-right: none; }
+          &:nth-child(4) {
+            border-top-right-radius: 4px;
+            border-bottom-right-radius: 4px;
+          }
+
+          &.check-table-button {
+            border: 1px solid #0183fa;
+            background-color: #0183fa;
+            color: #fff;
+          }
+        }
+      }
+    }
+    .button-color-a{
+      width:60px;
+      cursor: pointer;
+      margin-right:10px;
+      display: inline-block;
+      text-align: center;
+      line-height:24px;
+      border-radius:4px;
+      border: 1px solid #00B176;
+      color: #fff;
+      background-color: #00B176;
+    }
+    .button-color-b{
+      width:60px;
+      cursor: pointer;
+      display: inline-block;
+      text-align: center;
+      line-height:24px;
+      border-radius:4px;
+      border: 1px solid #E86452;
+      color: #fff;
+      background-color: #E86452;
+    }
+    .status-txt-a{
+      color: #F59A48;
+    }
+    .status-txt-b{
+      color: #00B176;
+    }
+    .status-txt-c{
+      color: #E86452;
+    }
+
+    ::v-deep .row-reject td {
+      background: #fffafa !important;
+    }
+
+    ::v-deep .el-table td {
+      padding: 10px 0;
+    }
+  }
+</style>

+ 1 - 1
src/views/safetyEducationExaminationNew/questionBankManagement/practiceSolvingProblems/index.vue

@@ -192,7 +192,7 @@
       },
       //知识点
       examElKnowledgePointTreeList(){
-        examElKnowledgePointTreeList({}).then(response => {
+        examElKnowledgePointTreeList({ dataScope:2 }).then(response => {
           const list = response.data || [];
           this.formatTreeData(list);
           this.$set(this, 'optionListC', list);

+ 32 - 9
src/views/safetyEducationExaminationNew/questionBankManagement/questionBankManagement/addPage.vue

@@ -22,7 +22,7 @@
               <span style="margin-left:15px;line-height:40px;color:#999;">{{newData.shareStatus==1?'共享开启,全校所有学院单位都可见':'不共享,仅本学院单位及上级都可见'}}</span>
             </el-form-item>
             <el-form-item label="试题类型" prop="questionType" label-width="100px">
-              <el-select v-model="newData.questionType" @change="answersScreening()" placeholder="试题类型" style="width: 180px">
+              <el-select v-model="newData.questionType" @change="answersScreening()" placeholder="试题类型" style="width: 160px">
                 <el-option
                   v-for="dict in optionListA"
                   :key="dict.value"
@@ -32,7 +32,7 @@
               </el-select>
             </el-form-item>
             <el-form-item label="难度" prop="difficulty" label-width="70px">
-              <el-select v-model="newData.difficulty" placeholder="难度" style="width: 180px">
+              <el-select v-model="newData.difficulty" placeholder="难度" style="width: 160px">
                 <el-option
                   v-for="dict in optionListC"
                   :key="dict.value"
@@ -41,9 +41,9 @@
                 />
               </el-select>
             </el-form-item>
-            <el-form-item label="知识点" prop="knowledgePointIds" label-width="90px">
+            <el-form-item label="知识点" prop="knowledgePointIds" label-width="85px">
               <el-cascader
-                style="width: 490px"
+                style="width: 220px"
                 v-model="newData.knowledgePointIds"
                 :options="optionListB"
                 :props="{
@@ -59,6 +59,16 @@
                 placeholder="知识点"
               ></el-cascader>
             </el-form-item>
+            <el-form-item label="试题分类" prop="disciplineTypeId" label-width="95px">
+              <el-select :disabled="showType" v-model="newData.disciplineTypeId" placeholder="试题分类" style="width:220px;">
+                <el-option
+                  v-for="item in optionListD"
+                  :key="item.id"
+                  :label="item.disciplineName"
+                  :value="item.id"
+                />
+              </el-select>
+            </el-form-item>
           </div>
         </div>
         <div class="title-content-box">
@@ -152,7 +162,7 @@
   //import { systemUserSelect } from "@/api/commonality/permission";
   import {
     examElKnowledgePointTreeList,examElQuestionBankDetail,
-    examElQuestionBankAdd,examElQuestionBankEdit,
+    examElQuestionBankAdd,examElQuestionBankEdit,examElDisciplineTypeList,
   } from "@/api/safetyEducationExaminationNew/index";
   import TinymceContainer from "@/components/tinymceContainer/index.vue";
   import { getToken } from "@/utils/auth";
@@ -167,9 +177,11 @@
         optionListA:[{value:1,label:'单选'},{value:2,label:'多选'},{value:3,label:'判断'}],
         optionListB:[],
         optionListC:[{value:1,label:'简单'},{value:2,label:'一般'},{value:3,label:'较难'},{value:4,label:'极难'}],
+        optionListD:[],
         newData: {
           questionType:null,
           knowledgePointIds:[],
+          disciplineTypeId:null,
           shareStatus:1,
           difficulty:null,
           questionContent:"",
@@ -192,6 +204,10 @@
             { required: true, message: "请选择知识点", trigger: "blur" },
             { required: true, message: "请选择知识点", validator: this.spaceJudgment, trigger: "blur" }
           ],
+          disciplineTypeId: [
+            { required: true, message: "请选择试题分类", trigger: "blur" },
+            { required: true, message: "请选择试题分类", validator: this.spaceJudgment, trigger: "blur" }
+          ],
           difficulty: [
             { required: true, message: "请选择难度", trigger: "blur" },
             { required: true, message: "请选择难度", validator: this.spaceJudgment, trigger: "blur" }
@@ -207,10 +223,8 @@
           // 假设最多只能选 2 个
           if (newVal && newVal.length > 10) {
             this.msgError('最多选择10个知识点')
-            this.$nextTick(() => {
-              // 将值回退到上一次的状态
-              this.newData.knowledgePointIds = oldVal;
-            });
+            // 将值回退到上一次的状态
+            this.newData.knowledgePointIds = oldVal;
           }
         },
         deep: true, // 关键:必须开启深度监听
@@ -225,6 +239,7 @@
         this.examElQuestionBankDetail();
       }
       this.examElKnowledgePointTreeList();
+      this.examElDisciplineTypeList();
     },
     methods: {
       examElQuestionBankDetail(){
@@ -233,6 +248,7 @@
             id:this.propsData.id,
             questionType:response.data.questionType,
             knowledgePointIds:response.data.knowledgePointIds,
+            disciplineTypeId:response.data.disciplineTypeId,
             shareStatus:response.data.shareStatus,
             difficulty:response.data.difficulty,
             questionContent:response.data.questionContent,
@@ -335,6 +351,7 @@
           questionContent:this.newData.questionContent,
           questionType:this.newData.questionType,
           knowledgePointIds:this.newData.knowledgePointIds,
+          disciplineTypeId:this.newData.disciplineTypeId,
           difficulty:this.newData.difficulty,
           correctAnswer:'',
           analysis:this.newData.analysis,
@@ -508,6 +525,12 @@
           this.$set(this, 'optionListB', list);
         });
       },
+      //查询全部专业类别
+      examElDisciplineTypeList() {
+        examElDisciplineTypeList({}).then(response => {
+          this.$set(this, 'optionListD', response.data)
+        })
+      },
       /** 将下标转换为字母*/
       getLetter(index) {
         if (typeof index !== 'number' || index < 0 || index >= 26) {

+ 1 - 1
src/views/safetyEducationExaminationNew/questionBankManagement/questionBankManagement/index.vue

@@ -283,7 +283,7 @@
         }
       },
       examElKnowledgePointTreeList(){
-        examElKnowledgePointTreeList({}).then(response => {
+        examElKnowledgePointTreeList({ dataScope:2 }).then(response => {
           const list = response.data || [];
           this.formatTreeData(list);
           this.$set(this, 'optionListB', list);

+ 4 - 3
src/views/safetyEducationExaminationNew/safetyEducation/knowledgeLearning/index.vue

@@ -63,7 +63,7 @@
             <div class="for-max-big-box scrollbar-box">
               <p class="null-text-p" v-if="!dataList[0]">暂无数据</p>
               <div class="for-big-box" v-for="(item,index) in dataList" :key="index" @click="tableButton(item)">
-                <img v-if="item.materialType == 1" :src="item.exampleImg">
+                <img v-if="item.materialType == 1&&item.exampleImg" :src="item.exampleImg">
                 <img v-else src="@/assets/ZDimages/basicsModules/image_986222064626770.png">
                 <div class="bottom-big-box">
                   <p class="name-p">{{item.coursewareName}}</p>
@@ -230,7 +230,8 @@
       },
       examElKnowledgePointTreeList(){
         let obj = {
-          knowledgePointIds : this.knowledgePointIds
+          knowledgePointIds : this.knowledgePointIds,
+          dataScope:1,
         };
         examElKnowledgePointTreeList(obj).then(response => {
           const list = response.data || [];
@@ -275,7 +276,7 @@
             margin:5px 0;
             padding:0 20px;
             text-align: center;
-            /*border-right:4px solid #ffffff;*/
+            border-left:6px solid #ffffff;
             cursor: pointer;
             font-weight:700;
             font-size:16px;

+ 98 - 27
src/views/safetyEducationExaminationNew/safetyTest/onlineExam/index.vue

@@ -73,6 +73,24 @@
         />
       </div>
     </div>
+    <el-dialog class="preExam-dialog"
+               :modal-append-to-body="false"
+               :close-on-click-modal="false" :close-on-press-escape="false"
+               title="考试承诺书"
+               :visible.sync="preExamDialogType"
+               v-if="preExamDialogType"
+               width="840px"
+               append-to-body>
+      <div style="height:600px;" class="scrollbar-box">
+        <richTextPdf ref="richTextPdf" :content="richTextHtml" />
+      </div>
+      <div slot="footer" class="dialog-footer dialog-footer-box">
+        <p class="dialog-footer-button-null"></p>
+        <p class="dialog-footer-button-border" v-if="preButtonType" @click="preExamDialogCancel">下载</p>
+        <p class="dialog-footer-button-primary" @click="examUserExamStart">确认</p>
+        <p class="dialog-footer-button-null"></p>
+      </div>
+    </el-dialog>
   </div>
 </template>
 <script>
@@ -81,8 +99,10 @@
   //import { getInfo } from "@/api/basicsModules/index";
   //import addPage from "./addPage.vue";
   import { examUserExamList,examUserExamTypeSelectExamType,examUserExamStart, } from "@/api/safetyEducationExaminationNew/index";
+  import richTextPdf from '@/views/safetyEducationExaminationNew/components/richTextPdf.vue';
   export default {
     name: 'index',
+    components: { richTextPdf },
     data() {
       return {
         tableButtonType: this.hasPermiDom(['demo:demo:detail', 'demo:demo:edit', 'demo:demo:del']),
@@ -105,7 +125,12 @@
         //数据数量
         total: 0,
         //组件传参
-        propsData: {}
+        propsData: {},
+        //考前承诺相关
+        preExamDialogType:false,
+        preButtonType:false,
+        richTextHtml: '',
+        preData:null,
       }
     },
     created() {
@@ -160,6 +185,76 @@
           this.$set(this, 'total', response.data.total)
         })
       },
+      //下载承诺书
+      preExamDialogCancel(){
+        this.msgSuccess('正在下载,请耐心等候')
+        this.$refs['richTextPdf'].downloadPDF();
+      },
+      //确认承诺-并考试
+      examUserExamStart(){
+        if(this.preData.attemptId){
+          this.$router.push({
+            path: '/startTheExam',
+            query: {
+              attemptId: this.preData.attemptId,
+              type: '1',
+              examKind:this.preData.examKind,
+            }
+          })
+        }else{
+          examUserExamStart({examId:this.preData.examId,examScene:1,}).then(response => {
+            if(response.data.attemptId){
+              this.$router.push({
+                path: '/startTheExam',
+                query: {
+                  examId: this.preData.examId,
+                  attemptId: response.data.attemptId,
+                  type: '1',
+                  examKind:this.preData.examKind,
+                }
+              })
+            }else{
+              this.msgError(response.message)
+            }
+          })
+        }
+      },
+      //开始考试判断是否有承诺书
+      setRichExamData(item){
+        if(item.commitmentEnabled == 1){
+          this.$set(this,'preButtonType',item.commitmentPrintEnabled == 1?true:false);
+          this.$set(this,'richTextHtml',item.commitmentContent);
+          this.$set(this,'preData',item);
+          this.$set(this,'preExamDialogType',true);
+        }else{
+          if(item.attemptId){
+            this.$router.push({
+              path: '/startTheExam',
+              query: {
+                attemptId: item.attemptId,
+                type: '1',
+                examKind:item.examKind,
+              }
+            })
+          }else{
+            examUserExamStart({examId:item.examId,examScene:1,}).then(response => {
+              if(response.data.attemptId){
+                this.$router.push({
+                  path: '/startTheExam',
+                  query: {
+                    examId: item.examId,
+                    attemptId: response.data.attemptId,
+                    type: '1',
+                    examKind:item.examKind,
+                  }
+                })
+              }else{
+                this.msgError(response.message)
+              }
+            })
+          }
+        }
+      },
       //操作按钮
       tableButton(type, item) {
         let self = this
@@ -204,32 +299,8 @@
             })
           }
         }else if(type == 4){
-          //开始考试
-          if(item.attemptId){
-            this.$router.push({
-              path: '/startTheExam',
-              query: {
-                examId: item.examId,
-                attemptId: item.attemptId,
-                examKind:item.examKind,
-              }
-            })
-          }else{
-            examUserExamStart({examId:item.examId,examScene:1,}).then(response => {
-              if(response.data.attemptId){
-                this.$router.push({
-                  path: '/startTheExam',
-                  query: {
-                    examId: item.examId,
-                    attemptId: response.data.attemptId,
-                    examKind:item.examKind,
-                  }
-                })
-              }else{
-                this.msgError(response.message)
-              }
-            })
-          }
+          //在线考试//补考
+          this.setRichExamData(item);
         }else if (type == 6) {
           //返回并刷新
           this.$set(this, 'pageType', 1)

+ 15 - 6
src/views/safetyEducationExaminationNew/safetyTest/practiceQuestions/index.vue

@@ -198,11 +198,19 @@
         }
       },
       examElKnowledgePointTreeList(){
-        examElKnowledgePointTreeList({}).then(response => {
+        let self = this;
+        examElKnowledgePointTreeList({ dataScope:2 }).then(response => {
           const list = response.data || [];
           this.formatTreeData(list);
-          this.$set(this, 'letDataList', list);
-          this.$set(this, 'optionListB', list[0].children?list[0].children:[]);
+          this.$set(this, 'letDataList', [{knowledgePointName:'全部',id:''},...list]);
+          if(this.$route.query.knowledgePointId){
+            for(let i=0;i<self.letDataList.length;i++){
+              if(self.letDataList[i].id == this.$route.query.knowledgePointId){
+                self.$set(self,'leftDataIndex',i);
+              }
+            }
+          }
+          // this.$set(this, 'optionListB', list[0].children?list[0].children:[]);
           this.getList();
         });
       },
@@ -230,7 +238,7 @@
             margin:5px 0;
             padding:0 20px;
             text-align: center;
-            border-right:4px solid #ffffff;
+            border-left:6px solid #ffffff;
             cursor: pointer;
             font-weight:700;
             font-size:16px;
@@ -246,8 +254,9 @@
             color: #ffffff;
           }
           .leftCheck{
-            color: #0183fa;
-            border-right:4px solid #0183fa;
+            color: #fff;
+            background-color: #0183fa;
+            /*border-right:4px solid #0183fa;*/
           }
         }
         .right-box{

+ 25 - 38
src/views/safetyEducationExaminationNew/workbench/ResourceCard.vue

@@ -39,35 +39,10 @@
             <p>学习资料</p>
             <p @click="goPage(2)">查看更多</p>
           </div>
-          <div class="for-max-big-box">
+          <div class="for-max-big-box" v-for="(item,index) in topRightList" :key="index" @click="goPage(4,item)">
             <p class="el-icon-notebook-2"></p>
-            <p>基础知识</p>
-            <p>120 题</p>
-          </div>
-          <div class="for-max-big-box">
-            <p class="el-icon-notebook-2"></p>
-            <p>基础知识</p>
-            <p>120 题</p>
-          </div>
-          <div class="for-max-big-box">
-            <p class="el-icon-notebook-2"></p>
-            <p>基础知识</p>
-            <p>120 题</p>
-          </div>
-          <div class="for-max-big-box">
-            <p class="el-icon-notebook-2"></p>
-            <p>基础知识</p>
-            <p>120 题</p>
-          </div>
-          <div class="for-max-big-box">
-            <p class="el-icon-notebook-2"></p>
-            <p>基础知识</p>
-            <p>120 题</p>
-          </div>
-          <div class="for-max-big-box">
-            <p class="el-icon-notebook-2"></p>
-            <p>基础知识</p>
-            <p>120 题</p>
+            <p>{{item.knowledgePointName}}</p>
+            <p>{{item.questionCount}} 题</p>
           </div>
         </div>
       </div>
@@ -84,7 +59,8 @@
       <div class="resource-body-bottom">
         <div class="for-max-big-box"  @click="tableButton(item)"
              v-for="(item,index) in bottomList" :key="index">
-          <img :src="item.exampleImg">
+          <img v-if="item.exampleImg" :src="item.exampleImg">
+          <img v-else src="@/assets/ZDimages/basicsModules/image_420782143223404.png">
           <div>
             <p>{{item.coursewareName}}</p>
             <p>{{secondsToMinutes(item.minLearningDuration)}}分钟 丨 {{item.creditHours}}学时 丨 {{item.points}}积分</p>
@@ -98,7 +74,7 @@
 <script>
   import {
     examElDisciplineTypeList,examUserCoursewareLearningRestart,
-    examUserPracticeList,examUserCoursewareLearningList,
+    examUserCoursewareLearningList,examElQuestionBankGroupByKnowledgePoint,
   } from "@/api/safetyEducationExaminationNew/index";
 export default {
   name: 'ResourceCard',
@@ -107,6 +83,7 @@ export default {
       topCheckIndex:0,
       bottomCheckIndex:0,
       tableList:[],
+      tableListBottom:[],
       topLeftList:[],
       topRightList:[],
       bottomList:[],
@@ -120,11 +97,14 @@ export default {
     topTableButton(index){
       if(this.topCheckIndex != index){
         this.$set(this,'topCheckIndex',index);
+        this.leftExamUserCoursewareLearningList();
+        this.examElQuestionBankGroupByKnowledgePoint();
       }
     },
     bottomTableButton(index){
       if(this.bottomCheckIndex != index){
         this.$set(this,'bottomCheckIndex',index);
+        this.rightExamUserCoursewareLearningList();
       }
     },
     tableButton(item){
@@ -154,8 +134,8 @@ export default {
         })
       }
     },
-    goPage(type){
-      //1.文章文档 2.刷题 3.视频
+    goPage(type,item){
+      //1.文章文档 2.刷题 3.视频 4.知识点指向刷题
       if(type == 1){
         this.$router.push({
           path: '/safetyEducationExaminationNew/safetyEducation/knowledgeLearning',
@@ -173,6 +153,13 @@ export default {
           path: '/safetyEducationExaminationNew/safetyEducation/knowledgeLearning',
           query: {}
         })
+      }else if(type == 4){
+        this.$router.push({
+          path: '/safetyEducationExaminationNew/safetyTest/practiceQuestions',
+          query: {
+            knowledgePointId:item.knowledgePointId,
+          }
+        })
       }
     },
     //文章
@@ -181,7 +168,7 @@ export default {
         page:1,
         pageSize:5,
         materialType:2,
-        knowledgePointId:this.knowledgePointId,
+        disciplineTypeId:this.tableList[this.topCheckIndex].id,
       }
       examUserCoursewareLearningList(obj).then(response => {
         this.$set(this, 'topLeftList', response.data.records)
@@ -193,20 +180,20 @@ export default {
         page:1,
         pageSize:6,
         materialType:1,
-        knowledgePointId:this.knowledgePointId,
+        disciplineTypeId:this.tableList[this.bottomCheckIndex].id,
       }
       examUserCoursewareLearningList(obj).then(response => {
         this.$set(this, 'bottomList', response.data.records)
       })
     },
     //知识点题库
-    examUserPracticeList(){
+    examElQuestionBankGroupByKnowledgePoint(){
       let obj = {
         page:1,
         pageSize:6,
-        knowledgePointId:this.knowledgePointId,
+        disciplineTypeId:this.tableList[this.topCheckIndex].id,
       }
-      examUserPracticeList(obj).then(response => {
+      examElQuestionBankGroupByKnowledgePoint(obj).then(response => {
         this.$set(this, 'topRightList', response.data.records)
       })
     },
@@ -216,7 +203,7 @@ export default {
         this.$set(this, 'tableList', response.data)
         this.leftExamUserCoursewareLearningList();
         this.rightExamUserCoursewareLearningList();
-        this.examUserPracticeList();
+        this.examElQuestionBankGroupByKnowledgePoint();
       })
     },
     //换算分钟

+ 124 - 56
src/views/safetyEducationExaminationNew/workbench/TaskCard.vue

@@ -9,7 +9,6 @@
         <div class="year-card-top-title-box">
           <p>学年数据统计</p>
           <p>按学年统计历年考试通过情况与成绩</p>
-          <!--<p v-if="yearType">成绩报告</p>-->
         </div>
         <div class="bottom-content-box scrollbar-box">
           <p class="null-p" v-if="!yearMinList[0]">暂无数据</p>
@@ -22,7 +21,7 @@
       <!--任务卡列表-->
       <div class="year-card-box for-max-big-box">
         <div class="year-card-top-title-box">
-          <p>学习任务</p>
+          <p>学习考试任务</p>
           <p>待完成的学习任务</p>
         </div>
         <div class="bottom-content-box scrollbar-box">
@@ -52,37 +51,23 @@
               <p>补考</p>
               <p>未完成</p>
             </div>
-            <p class="time-p">{{parseTime(item.startTime,"{y}-{m}-{d} {h}:{i}")}} 至 {{parseTime(item.startTime,"{y}-{m}-{d} {h}:{i}")}}</p>
+            <p class="time-p">{{parseTime(item.startTime,"{y}-{m}-{d} {h}:{i}")}} 至 {{parseTime(item.endTime,"{y}-{m}-{d} {h}:{i}")}}</p>
           </div>
         </div>
       </div>
-      <!--学习任务卡-->
-      <!--<div class="study-card-box for-max-big-box">-->
-        <!--<div class="year-card-top-title-box">-->
-          <!--<p>学习任务</p>-->
-          <!--<p>待完成的学习任务</p>-->
-        <!--</div>-->
-        <!--<div class="bottom-content-box scrollbar-box">-->
-          <!--<p class="null-p" v-if="!studyMinList[0]">暂无数据</p>-->
-          <!--<div class="bottom-big-box-2" v-for="(item,index) in studyMinList" @click="studyTableButton(item)">-->
-            <!--<p>{{item.courseName}}</p>-->
-            <!--<p >{{item.deptName}}丨{{item.learnStatus==0?'待完成':(item.learnStatus==1?'进行中':(item.learnStatus==2?'已完成':''))}}</p>-->
-          <!--</div>-->
-        <!--</div>-->
-      <!--</div>-->
       <!--模拟练习卡-->
       <div class="examination-card-box for-max-big-box">
         <div class="year-card-top-title-box">
           <p>模拟练习</p>
           <p>刷题进度和正确率</p>
         </div>
-        <div class="bottom-content-box scrollbar-box" style="cursor: pointer;" @click="goPracticeQuestions()">
+        <div class="bottom-content-box scrollbar-box">
           <div class="bottom-big-box-6">
-            <p>156 / 200</p>
+            <p>{{summaryData.completedCount}} / {{summaryData.totalCount}}</p>
             <p >已完成题目数 / 题库总数</p>
           </div>
           <div class="bottom-big-box-6">
-            <p>89%</p>
+            <p>{{summaryData.correctRate}}%</p>
             <p >当前模拟练习正确率</p>
           </div>
         </div>
@@ -90,11 +75,14 @@
       <!--在线任务卡-->
       <div class="for-max-big-box" v-for="(item,index) in dataList" :key="index">
         <div class="year-card-top-title-box">
-          <p>{{item.examTypeName}}</p>
+          <p>{{item.examName}}</p>
           <p v-if="item.examKind==1&&item.conditionType != 4">满足前置条件后可参加考试</p>
           <p v-if="item.examKind==1&&item.conditionType == 4">
-            <span :class="!item.isPassed&&item.maxScore==0?'color-A':(!item.isPassed&&item.maxScore>0?'color-B':(item.isPassed?'color-C':''))">{{!item.isPassed&&item.maxScore==0?'未开始':(!item.isPassed&&item.maxScore>0?'未通过':(item.isPassed?'已通过':''))}}</span>
-            <span style="margin-left:20px;">{{item.maxScore>0?'成绩:'+item.maxScore+'分':''}}</span>
+            <span>{{parseTime(item.startTime,"{y}-{m}-{d} {h}:{i}")}} 至 {{parseTime(item.endTime,"{y}-{m}-{d} {h}:{i}")}}</span>
+          </p>
+          <p v-if="item.examKind==1&&item.conditionType == 4">
+            <span :class="!item.isPassed&&item.maxScore==0?'color-A':(!item.isPassed&&item.maxScore>0?'color-B':(item.isPassed?'color-C':''))">{{!item.isPassed&&item.maxScore==0?'未考试':(!item.isPassed&&item.maxScore>0?'未通过':(item.isPassed?'已通过':''))}}</span>
+            <span v-if="item.maxScore>0" style="margin-left:15px;">{{item.maxScore>0?'成绩:'+item.maxScore+'分':''}}</span>
           </p>
         </div>
         <div class="bottom-content-box scrollbar-box">
@@ -115,7 +103,7 @@
             <p class="colorC" v-if="item.examStudy">待完成学习任务后解锁</p>
           </div>
           <div class="bottom-big-box-4">
-            <p>考试状态</p>
+            <p>在线考试</p>
             <p class="colorA" v-if="item.examKind==1&&item.conditionType == 4" @click="tableButton(4,item)">开始考试</p>
             <p class="colorC" v-if="item.examKind==1&&item.conditionType != 4">待通过模拟考试后解锁</p>
             <p class="colorB" v-if="item.examKind==2">考试未通过</p>
@@ -127,6 +115,24 @@
         </div>
       </div>
     </div>
+    <el-dialog class="preExam-dialog"
+               :modal-append-to-body="false"
+               :close-on-click-modal="false" :close-on-press-escape="false"
+               title="考试承诺书"
+               :visible.sync="preExamDialogType"
+               v-if="preExamDialogType"
+               width="840px"
+               append-to-body>
+      <div style="height:600px;" class="scrollbar-box">
+        <richTextPdf ref="richTextPdf" :content="richTextHtml" />
+      </div>
+      <div slot="footer" class="dialog-footer dialog-footer-box">
+        <p class="dialog-footer-button-null"></p>
+        <p class="dialog-footer-button-border" v-if="preButtonType" @click="preExamDialogCancel">下载</p>
+        <p class="dialog-footer-button-primary" @click="examUserExamStart">确认</p>
+        <p class="dialog-footer-button-null"></p>
+      </div>
+    </el-dialog>
   </div>
 </template>
 
@@ -134,10 +140,12 @@
   import { Encrypt,Decrypt} from '@/utils/secret'
   import {
     examUserCoursewareLearningRestart,examElLabEduRelationAcademicYearProgress,
-    examUserExamStudyTaskList,examUserExamStart,
+    examUserExamStudyTaskList,examUserExamStart,examUserPracticeSummary,
   } from "@/api/safetyEducationExaminationNew/index";
+  import richTextPdf from '@/views/safetyEducationExaminationNew/components/richTextPdf.vue';
 export default {
   name: 'TaskCard',
+  components: { richTextPdf },
   data() {
     return {
       //学年数据
@@ -147,6 +155,16 @@ export default {
       examinationMinList:[],
       dataList:[],
       dataList2:[],
+      summaryData:{
+        totalCount:0,
+        completedCount:0,
+        correctRate:0,
+      },
+      //考前承诺相关
+      preExamDialogType:false,
+      preButtonType:false,
+      richTextHtml: '',
+      preData:null,
     }
   },
   created() {
@@ -157,10 +175,81 @@ export default {
   },
   methods: {
     initialization(){
+      this.examUserPracticeSummary();
       this.examElLabEduRelationAcademicYearProgress();
       this.examUserExamStudyTaskList();
       this.examUserExamStudyTaskList2();
     },
+    //下载承诺书
+    preExamDialogCancel(){
+      this.msgSuccess('正在下载,请耐心等候')
+      this.$refs['richTextPdf'].downloadPDF();
+    },
+    //确认承诺-并考试
+    examUserExamStart(){
+      if(this.preData.attemptId){
+        this.$router.push({
+          path: '/startTheExam',
+          query: {
+            attemptId: this.preData.attemptId,
+            type: '1',
+            examKind:this.preData.examKind,
+          }
+        })
+      }else{
+        examUserExamStart({examId:this.preData.examId,examScene:1,}).then(response => {
+          if(response.data.attemptId){
+            this.$router.push({
+              path: '/startTheExam',
+              query: {
+                examId: this.preData.examId,
+                attemptId: response.data.attemptId,
+                type: '1',
+                examKind:this.preData.examKind,
+              }
+            })
+          }else{
+            this.msgError(response.message)
+          }
+        })
+      }
+    },
+    //开始考试判断是否有承诺书
+    setRichExamData(item){
+      if(item.commitmentEnabled == 1){
+        this.$set(this,'preButtonType',item.commitmentPrintEnabled == 1?true:false);
+        this.$set(this,'richTextHtml',item.commitmentContent);
+        this.$set(this,'preData',item);
+        this.$set(this,'preExamDialogType',true);
+      }else{
+        if(item.attemptId){
+          this.$router.push({
+            path: '/startTheExam',
+            query: {
+              attemptId: item.attemptId,
+              type: '1',
+              examKind:item.examKind,
+            }
+          })
+        }else{
+          examUserExamStart({examId:item.examId,examScene:1,}).then(response => {
+            if(response.data.attemptId){
+              this.$router.push({
+                path: '/startTheExam',
+                query: {
+                  examId: item.examId,
+                  attemptId: response.data.attemptId,
+                  type: '1',
+                  examKind:item.examKind,
+                }
+              })
+            }else{
+              this.msgError(response.message)
+            }
+          })
+        }
+      }
+    },
     //跳转刷题练习
     goPracticeQuestions(){
       this.$router.push({
@@ -222,32 +311,7 @@ export default {
         }
       }else if(type == 4){
         //在线考试//补考
-        if(item.attemptId){
-          this.$router.push({
-            path: '/startTheExam',
-            query: {
-              attemptId: item.attemptId,
-              type: '1',
-              examKind:item.examKind,
-            }
-          })
-        }else{
-          examUserExamStart({examId:item.examId,examScene:1,}).then(response => {
-            if(response.data.attemptId){
-              this.$router.push({
-                path: '/startTheExam',
-                query: {
-                  examId: item.examId,
-                  attemptId: response.data.attemptId,
-                  type: '1',
-                  examKind:item.examKind,
-                }
-              })
-            }else{
-              this.msgError(response.message)
-            }
-          })
-        }
+        this.setRichExamData(item);
       }
     },
     //查询学年进度列表
@@ -280,6 +344,12 @@ export default {
         this.$set(this, 'dataList2', response.data.records)
       })
     },
+    //在线考试
+    examUserPracticeSummary(){
+      examUserPracticeSummary({}).then(response => {
+        this.$set(this, 'summaryData', response.data)
+      })
+    },
   },
 }
 </script>
@@ -357,13 +427,11 @@ export default {
           cursor: pointer;
           font-size:14px;
           line-height:30px;
-          background-color: #0183fa;
-          color:#fff;
-          border-radius:6px;
+          color:#60708a;
+          text-align: right;
           position: absolute;
           top:10px;
-          right:10px;
-          padding:0 10px;
+          right:20px;
         }
         .color-A{
           color:#999;

+ 3 - 20
src/views/safetyEducationExaminationNew/workbench/index.vue

@@ -1,21 +1,13 @@
 <!-- 工作台 -->
 <template>
   <div class="app-container workbench">
-    <div class="page-container workbenchPage scrollbar-box" v-if="pageType === 1">
+    <div class="page-container workbenchPage scrollbar-box">
       <!-- 用户信息卡片 -->
       <user-info-card/>
       <!-- 工作台任务卡片 -->
       <task-card/>
       <!-- 实验室安全素养资源卡 -->
       <resource-card/>
-      <!-- 实验室绑定申请 -->
-      <lab-binding-apply/>
-      <!-- 考试情况统计 -->
-      <exam-situation v-if="adminType"/>
-      <!-- 实验室与人员匹配 -->
-      <lab-person-match v-if="adminType"/>
-      <!-- 学时统计 -->
-      <study-hour-stats v-if="adminType"/>
     </div>
   </div>
 </template>
@@ -25,10 +17,6 @@
   import UserInfoCard from './UserInfoCard.vue'
   import TaskCard from './TaskCard.vue'
   import ResourceCard from './ResourceCard.vue'
-  import LabBindingApply from './LabBindingApply.vue'
-  import ExamSituation from './ExamSituation.vue'
-  import LabPersonMatch from './LabPersonMatch.vue'
-  import StudyHourStats from './StudyHourStats.vue'
 
   export default {
     name: 'index',
@@ -36,22 +24,17 @@
       UserInfoCard,
       TaskCard,
       ResourceCard,
-      LabBindingApply,
-      ExamSituation,
-      LabPersonMatch,
-      StudyHourStats,
     },
     data() {
       return {
-        pageType: 1,
-        adminType:false,
+
       }
     },
     created() {
 
     },
     mounted() {
-      this.$set(this,'adminType',itoOrVideoLimits());
+
     },
     methods: {