zhangjie před 4 roky
rodič
revize
85bd68c8eb

+ 120 - 2
src/api/invigilation.js

@@ -10,6 +10,23 @@ export function invigilateList(datas) {
     {}
   );
 }
+export function invigilateVideoList(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post(
+    "/api/admin/invigilate/list/video?" + object2QueryString(data),
+    {}
+  );
+}
+
+// 强制/手动交卷接口
+export function invigilateFinish(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post("/api/admin/invigilate/finish", data);
+}
+// 结束监考接口
+export function invigilateExamFinish(examId) {
+  return httpApp.post("/api/admin/invigilate/exam/finish?examId=" + examId, {});
+}
 export function invigilateDetail(recordId) {
   return httpApp.post(
     "/api/admin/invigilate/list/detail?examRecordId=" + recordId,
@@ -17,10 +34,23 @@ export function invigilateDetail(recordId) {
   );
 }
 
+// 考试列表
 export function examList(datas) {
   const data = pickBy(datas, (v) => v !== "");
   return httpApp.post("/api/admin/exam/list?" + object2QueryString(data), {});
 }
+// 监考老师::考试批次列表
+export function examBatchList(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post(
+    "/api/admin/sys/exam/query?" + object2QueryString(data),
+    {}
+  );
+}
+// 考试属性统计接口
+export function examPropCount(examId) {
+  return httpApp.post("/api/admin/exam/prop/count?examId=" + examId, {});
+}
 
 export function warningStudentDetail({ recordId }) {
   const data = {
@@ -61,10 +91,98 @@ export function communicationOver(callCancelBackendMobile) {
 }
 
 // invigilation-detail
-export function invigilationHistoryList({ pageNumber = 1, pageSize = 10 }) {
-  const data = pickBy({ pageNumber, pageSize }, (v) => v !== "");
+export function invigilationHistoryList(datas) {
+  const data = pickBy(datas, (v) => v !== "");
   return httpApp.post(
     "/api/admin/invigilate/history/list?" + object2QueryString(data),
     {}
   );
+
+  // {
+  //   "breachStatus": 0,
+  //   "examActivityId": 0,
+  //   "examId": 0,
+  //   "examRecordId": 0,
+  //   "examStudentId": 0,
+  //   "exceptionCount": 0,
+  //   "finishType": "",
+  //   "identity": "",
+  //   "multipleFaceCount": 0,
+  //   "name": "",
+  //   "roomCode": "",
+  //   "roomName": "",
+  //   "status": "",
+  //   "statusCode": "",
+  //   "updateTime": "",
+  //   "warningCount": 0
+  // }
+}
+
+// waining-manage
+export function invigilationWarningList(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post(
+    "/api/admin/invigilate/warn/list?" + object2QueryString(data),
+    {}
+  );
+}
+// TODO:批量处理违纪
+export function batchInvigilation(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post(
+    "/api/admin/invigilate/warn/list?" + object2QueryString(data),
+    {}
+  );
+}
+// TODO:清除未阅
+export function clearInvigilationUnreadWarningList(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post(
+    "/api/admin/invigilate/warn/list?" + object2QueryString(data),
+    {}
+  );
+}
+
+// reexam-apply
+export function reexamApplyList(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post(
+    "/api/admin/invigilate/reexam/list?" + object2QueryString(data),
+    {}
+  );
+}
+export function applyReexam(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post("/api/admin/invigilate/reexam/apply", data);
+}
+
+// reexam-pending
+export function reexamPendingList(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post(
+    "/api/admin/invigilate/reexam/list_not_done?" + object2QueryString(data),
+    {}
+  );
+}
+export function checkReexamApply(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post("/api/admin/invigilate/reexam/auditing", data);
+}
+
+// reexam-checked
+export function reexamCheckedList(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post(
+    "/api/admin/invigilate/reexam/list_done?" + object2QueryString(data),
+    {}
+  );
+}
+
+// progress-detail
+export function progressDetailList(datas) {
+  const data = pickBy(datas, (v) => v !== "");
+  return httpApp.post(
+    "/api/admin/invigilate/progress/list?" + object2QueryString(data),
+    {}
+  );
 }

+ 0 - 0
src/assets/icon-bat.png → src/assets/icon-seal.png


binární
src/assets/icon-upload.png


+ 18 - 0
src/constant/constants.js

@@ -13,3 +13,21 @@ export const INVIGILATOR_IMPORT_TEMPLATE_DOWNLOAD_URL =
   "/file/考场监考老师导入.xlsx";
 export const EXAM_STUDENT_IMPORT_TEMPLATE_DOWNLOAD_URL =
   "/file/考生导入_在线考试.xlsx";
+
+export const FINISH_TYPE = {
+  MANUAL: "手动收卷",
+  AUTO: "正常交卷",
+  BREACH: "强制收卷",
+  INTERRUPT: "系统清卷",
+};
+
+export const REEXAM_TYPE = {
+  0: "批次内",
+  1: "换批次",
+};
+
+export const REEXAM_REASON = {
+  EXCEPTION_TIME_OUT: "异常处理时效过期",
+  BREAK_TIME_OUT: "断点续考次数用完",
+  INVIGILATE_MISS: "监考人员误操作",
+};

+ 8 - 106
src/features/invigilation/ExamInvigilation/ExamInvigilation.vue

@@ -201,6 +201,11 @@
     </div>
     <div class="invigilation-list">
       <h3>实时监控台</h3>
+      <invigilation-student
+        v-for="item in students"
+        :key="item.examStudentId"
+        :data="item"
+      ></invigilation-student>
       <div class="invigilation-student invigilation-student-warning">
         <div class="student-video"></div>
         <div class="student-info">
@@ -231,10 +236,11 @@
 
 <script>
 import EchartRender from "../common/EchartRender";
+import InvigilationStudent from "../common/InvigilationStudent";
 
 export default {
   name: "exam-invigilation",
-  components: { EchartRender },
+  components: { EchartRender, InvigilationStudent },
   data() {
     return {
       onlineData: {
@@ -354,6 +360,7 @@ export default {
         type: "light",
       },
       chartDataReady: false,
+      students: [],
     };
   },
   computed: {
@@ -443,111 +450,6 @@ export default {
     line-height: 25px;
     margin-bottom: 20px;
   }
-  .invigilation-student {
-    padding: 10px;
-    position: relative;
-    margin-bottom: 20px;
-
-    &-warning {
-      &::before {
-        content: "";
-        display: block;
-        position: absolute;
-        top: 0;
-        left: 0;
-        width: 50px;
-        height: 50px;
-        background-image: url(../../../assets/bg-discipline.png);
-        background-size: 100% 100%;
-        z-index: 99;
-      }
-      &::after {
-        content: "";
-        display: block;
-        position: absolute;
-        top: 4px;
-        right: 4px;
-        width: 12px;
-        height: 12px;
-
-        border: 2px solid #fff;
-        border-radius: 50%;
-        background: #fe5863;
-        z-index: 99;
-      }
-    }
-
-    &-netbreak {
-      .icon-net-break {
-        display: inline-block !important;
-      }
-      .student-time {
-        background-color: #fe5863 !important;
-      }
-    }
-  }
-  .student-video {
-    height: 150px;
-    border-radius: 6px;
-    background: #626a82;
-    margin-bottom: 20px;
-    position: relative;
-
-    &::before {
-      content: "";
-      display: block;
-      position: absolute;
-      top: 50%;
-      left: 50%;
-      width: 32px;
-      height: 20px;
-      margin-left: -16px;
-      margin-top: -10px;
-      background-image: url(../../../assets/icon-video.png);
-      background-size: 100% 100%;
-    }
-
-    > video {
-      width: 100%;
-      height: 100%;
-    }
-  }
-  .student-info {
-    position: relative;
-    padding: 0 10px 10px;
-    .student-time {
-      position: absolute;
-      height: 24px;
-      top: 0;
-      right: 0;
-      padding: 4px 12px;
-      border-radius: 12px;
-      background: #abb8c9;
-      color: #fff;
-      font-size: 12px;
-      > i {
-        margin-right: 5px;
-      }
-    }
-
-    > h6 {
-      font-size: 18px;
-      line-height: 25px;
-      margin-bottom: 8;
-      > .icon {
-        margin-top: -4px;
-        margin-left: 8px;
-        display: none;
-      }
-    }
-    > p {
-      font-size: 14px;
-      font-weight: 400;
-      color: #626a82;
-      line-height: 20px;
-      margin: 0;
-    }
-  }
 }
 .invigilation-analysis {
   .part-box-head-left h1 {

+ 126 - 73
src/features/invigilation/InvigilationDetail/InvigilationDetail.vue

@@ -14,7 +14,7 @@
         >
           <el-form-item>
             <el-select
-              v-model="filter.batchId"
+              v-model="filter.examId"
               placeholder="请选择批次"
               clearable
             >
@@ -28,7 +28,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.examroom"
+              v-model="filter.examActivityId"
               placeholder="请选择场次"
               clearable
             >
@@ -42,7 +42,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.examroom"
+              v-model="filter.examActivityId"
               placeholder="请选择考场"
               clearable
             >
@@ -56,7 +56,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.courseCode"
               placeholder="请选择科目"
               clearable
             >
@@ -70,14 +70,14 @@
           </el-form-item>
           <el-form-item>
             <el-input
-              v-model.trim="filter.content"
+              v-model.trim="filter.name"
               placeholder="姓名/证件号"
               clearable
             ></el-input>
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.finishType"
               placeholder="交卷方式"
               clearable
             >
@@ -90,11 +90,7 @@
             </el-select>
           </el-form-item>
           <el-form-item>
-            <el-select
-              v-model="filter.subjectId"
-              placeholder="筛选状态"
-              clearable
-            >
+            <el-select v-model="filter.status" placeholder="筛选状态" clearable>
               <el-option
                 v-for="item in subjects"
                 :key="item.id"
@@ -105,7 +101,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.breachStatus"
               placeholder="违纪/缺考"
               clearable
             >
@@ -117,53 +113,56 @@
               ></el-option>
             </el-select>
           </el-form-item>
-          <el-form-item label="陌生人脸">
+          <el-form-item label="陌生人脸" v-if="showAdvancedFilter">
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.strangePersonDown"
+              v-model.trim="filter.minMultipleFaceCount"
               placeholder="下限"
               :controls="false"
             ></el-input-number>
             <span class="line-split">-</span>
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.strangePersonUp"
+              v-model.trim="filter.maxMultipleFaceCount"
               placeholder="上限"
               :controls="false"
             ></el-input-number>
           </el-form-item>
-          <el-form-item label="异常处理">
+          <el-form-item label="异常处理" v-if="showAdvancedFilter">
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.exceptionDown"
+              v-model.trim="filter.minExceptionCount"
               placeholder="下限"
               :controls="false"
             ></el-input-number>
             <span class="line-split">-</span>
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.exceptionUp"
+              v-model.trim="filter.maxExceptionCount"
               placeholder="上限"
               :controls="false"
             ></el-input-number>
           </el-form-item>
-          <el-form-item label="预警数">
+          <el-form-item label="预警数" v-if="showAdvancedFilter">
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.warningDown"
+              v-model.trim="filter.minWarningCount"
               placeholder="下限"
               :controls="false"
             ></el-input-number>
             <span class="line-split">-</span>
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.warningUp"
+              v-model.trim="filter.maxWarningCount"
               placeholder="上限"
               :controls="false"
             ></el-input-number>
           </el-form-item>
           <el-form-item>
             <el-button type="primary" @click="toPage(1)">查询</el-button>
+            <el-button type="primary" @click="changeFilter">{{
+              showAdvancedFilter ? "隐藏高级查询" : "高级查询"
+            }}</el-button>
           </el-form-item>
         </el-form>
       </div>
@@ -171,16 +170,40 @@
       <div class="part-filter-info">
         <div class="part-filter-info-main summary-line">
           <p class="summary-line-item">
-            <i class="icon icon-users"></i
-            ><span class="line-name">全部应考</span><span>50人</span>
+            <i class="icon icon-users"></i>
+            <el-popover
+              placement="top-start"
+              width="200"
+              trigger="hover"
+              content="全部应考:参加考试的全部考生。"
+            >
+              <span class="line-name" slot="reference">全部应考</span>
+            </el-popover>
+            <span>{{ examPropData.allCount }}人</span>
           </p>
           <p class="summary-line-item">
-            <i class="line-point line-point-info"></i
-            ><span class="line-name">已登录</span><span>5人</span>
+            <i class="line-point line-point-info"></i>
+            <el-popover
+              placement="top-start"
+              width="200"
+              trigger="hover"
+              content="已登录:已成功登录考生端的考生。"
+            >
+              <span class="line-name" slot="reference">已登录</span>
+            </el-popover>
+            <span>{{ examPropData.loginCount }}人</span>
           </p>
           <p class="summary-line-item">
-            <i class="line-point line-point-success"></i
-            ><span class="line-name">已待考</span><span>3人</span>
+            <i class="line-point line-point-success"></i>
+            <el-popover
+              placement="top-start"
+              width="200"
+              trigger="hover"
+              content="已待考:已进入待考界面等待开考的考生。"
+            >
+              <span class="line-name" slot="reference">已待考</span>
+            </el-popover>
+            <span>{{ examPropData.prepareCount }}人</span>
           </p>
           <p class="summary-line-item">
             <i class="line-point line-point-primary"></i>
@@ -188,15 +211,24 @@
               placement="top-start"
               width="200"
               trigger="hover"
-              content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。"
+              content="考试中:正在答题的考生。"
             >
               <span class="line-name" slot="reference">考试中</span>
             </el-popover>
-            <span>2人</span>
+            <span>{{ examPropData.notComplete }}人</span>
           </p>
           <p class="summary-line-item">
             <i class="line-point line-point-danger"></i>
-            <span class="line-name">已交卷</span><span>1人</span>
+            <!-- <el-popover
+              placement="top-start"
+              width="200"
+              trigger="hover"
+              content="已交卷:考生交卷成功,结束考试。"
+            >
+              <span class="line-name" slot="reference">已交卷</span>
+            </el-popover> -->
+            <span class="line-name">已交卷</span>
+            <span>{{ examPropData.alreadyComplete }}人</span>
           </p>
         </div>
       </div>
@@ -205,21 +237,26 @@
     <el-table ref="TableList" :data="dataList">
       <el-table-column prop="batchName" label="批次"></el-table-column>
       <el-table-column prop="examName" label="场次"></el-table-column>
-      <el-table-column prop="examroom" label="考场"> </el-table-column>
+      <el-table-column prop="roomName" label="考场"> </el-table-column>
       <el-table-column prop="examId" label="考试ID"></el-table-column>
-      <el-table-column prop="stdCardNo" label="证件号"></el-table-column>
-      <el-table-column prop="stdName" label="姓名"></el-table-column>
+      <el-table-column prop="identity" label="证件号"></el-table-column>
+      <el-table-column prop="name" label="姓名"></el-table-column>
       <el-table-column prop="subjectName" label="联系电话"></el-table-column>
-      <el-table-column prop="subjectCode" label="科目(代码)"></el-table-column>
-      <el-table-column prop="strangeNumber" label="状态"></el-table-column>
-      <el-table-column prop="strangeNumber" label="交卷方式"></el-table-column>
-      <el-table-column prop="strangeNumber" label="陌生人脸"></el-table-column>
+      <el-table-column prop="courseNameCode" label="科目(代码)">
+      </el-table-column>
+      <el-table-column prop="status" label="状态"></el-table-column>
+      <el-table-column prop="finishType" label="交卷方式">
+        <template slot-scope="scope">
+          <div>{{ FINISH_TYPE[scope.row.finishType] }}</div>
+        </template>
+      </el-table-column>
       <el-table-column
-        prop="exceptionNumber"
-        label="异常处理"
+        prop="multipleFaceCount"
+        label="陌生人脸"
       ></el-table-column>
-      <el-table-column prop="warningNumber" label="预警数"></el-table-column>
-      <el-table-column prop="isDiscipline" label="违纪"></el-table-column>
+      <el-table-column prop="exceptionCount" label="异常处理"></el-table-column>
+      <el-table-column prop="warningCount" label="预警数"></el-table-column>
+      <el-table-column prop="breachStatus" label="违纪"></el-table-column>
       <el-table-column label="操作">
         <template slot-scope="scope">
           <el-button
@@ -247,59 +284,75 @@
 </template>
 
 <script>
+import { invigilationHistoryList, examPropCount } from "@/api/invigilation";
+import { FINISH_TYPE } from "@/constant/constants";
+
 export default {
   name: "invigilation-detail",
   data() {
     return {
       filter: {
-        batchId: null,
-        examroom: null,
-        subjectId: null,
+        examId: null,
+        examActivityId: null,
+        courseCode: null,
         auditStatus: null,
-        content: "",
-        strangePersonUp: null,
-        strangePersonDown: null,
-        exceptionUp: null,
-        exceptionDown: null,
-        warningUp: null,
-        warningDown: null,
+        name: "",
+        maxMultipleFaceCount: null,
+        minMultipleFaceCount: null,
+        maxExceptionCount: null,
+        minExceptionCount: null,
+        maxWarningCount: null,
+        minWarningCount: null,
       },
+      showAdvancedFilter: false,
+      FINISH_TYPE,
       current: 1,
       total: 0,
       size: 10,
       batchs: [],
       exams: [],
       subjects: [],
-      dataList: [
-        {
-          id: 1,
-          batchName: "第一批次",
-          examName: "第一场次",
-          examroom: "第一考场",
-          examId: "123456",
-          stdCardNo: "000000000000000008",
-          stdName: "张龙龙",
-          subjectName: "大学英语",
-          subjectCode: "10006",
-          strangeNumber: "0",
-          exceptionNumber: "2",
-          warningNumber: "2",
-          isDiscipline: "",
-          auditStatus: "未阅",
-        },
-      ],
+      dataList: [],
+      examPropData: {},
     };
   },
   methods: {
-    getList() {},
-    toPage() {},
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current - 1,
+        pageSize: this.size,
+      };
+
+      const res = await invigilationHistoryList(datas);
+
+      this.dataList = res.data.data.records;
+      this.total = res.data.data.records.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    async getExamPropCount() {
+      this.examPropData = await examPropCount(this.filter.examId);
+    },
+    changeFilter() {
+      this.showAdvancedFilter = !this.showAdvancedFilter;
+      if (!this.showAdvancedFilter) {
+        this.filter.maxMultipleFaceCount = null;
+        this.filter.minMultipleFaceCount = null;
+        this.filter.maxExceptionCount = null;
+        this.filter.minExceptionCount = null;
+        this.filter.maxWarningCount = null;
+        this.filter.minWarningCount = null;
+      }
+    },
     cleanUnread() {},
     batchAction() {},
     toDetail(row) {
-      console.log(row);
       this.$router.push({
         name: "WainingDetail",
-        params: { recordId: row.id },
+        params: { recordId: row.examRecordId },
       });
     },
   },

+ 51 - 58
src/features/invigilation/ProgressDetail/ProgressDetail.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="ProgressDetail">
+  <div class="progress-detail">
     <div class="part-box-head">
       <div class="part-box-head-left"><h1>进度查询</h1></div>
     </div>
@@ -8,7 +8,7 @@
         <el-form ref="FilterForm" label-position="left" inline>
           <el-form-item>
             <el-select
-              v-model="filter.batchId"
+              v-model="filter.examId"
               placeholder="请选择批次"
               clearable
             >
@@ -22,7 +22,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.examroom"
+              v-model="filter.roomCode"
               placeholder="请选择考场"
               clearable
             >
@@ -36,7 +36,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.courseCode"
               placeholder="请选择科目"
               clearable
             >
@@ -50,7 +50,7 @@
           </el-form-item>
           <el-form-item>
             <el-input
-              v-model.trim="filter.content"
+              v-model.trim="filter.name"
               placeholder="姓名/证件号"
               clearable
             ></el-input>
@@ -77,7 +77,7 @@
               placement="bottom-start"
               width="200"
               trigger="hover"
-              content="通讯故障。"
+              content="已待考:已进入待考界面等待开考的考生"
             >
               <span class="line-name" slot="reference">已待考</span>
             </el-popover>
@@ -89,7 +89,7 @@
           </p>
         </div>
         <div class="part-filter-info-sub">
-          <el-button type="primary" icon="el-icon-download" @click="toExport"
+          <el-button type="primary" icon="icon icon-upload" @click="toExport"
             >导出查询结果</el-button
           >
         </div>
@@ -97,26 +97,27 @@
     </div>
 
     <el-table ref="TableList" :data="dataList">
-      <el-table-column prop="examroom" label="批次名称(代码)">
+      <el-table-column prop="roomCode" label="批次名称(代码)">
+      </el-table-column>
+      <el-table-column prop="examName" label="场次名称(代码)"></el-table-column>
+      <el-table-column prop="roomName" label="考场名称(代码)">
+        <span slot-scope="scope"
+          >{{ scope.row.roomName }}({{ scope.row.roomCode }})</span
+        >
+      </el-table-column>
+      <el-table-column prop="identity" label="证件号"></el-table-column>
+      <el-table-column prop="name" label="姓名"></el-table-column>
+      <el-table-column prop="mobileNumber" label="联系电话"></el-table-column>
+      <el-table-column prop="courseName" label="科目(代码)">
+        <span slot-scope="scope"
+          >{{ scope.row.courseName }}({{ scope.row.courseCode }})</span
+        >
       </el-table-column>
       <el-table-column
-        prop="stdCardNo"
-        label="场次名称(代码)"
-      ></el-table-column>
-      <el-table-column prop="stdName" label="考场名称(代码)"></el-table-column>
-      <el-table-column prop="subjectCode" label="证件号"></el-table-column>
-      <el-table-column prop="subjectCode" label="姓名"></el-table-column>
-      <el-table-column prop="subjectCode" label="联系电话"></el-table-column>
-      <el-table-column prop="subjectCode" label="科目(代码)"></el-table-column>
-      <el-table-column
-        prop="exceptionNumber"
-        label="异常处理"
-      ></el-table-column>
-      <el-table-column
-        prop="strangeNumber"
+        prop="leftExamCount"
         label="剩余考试次数"
       ></el-table-column>
-      <el-table-column prop="warningNumber" label="完成状态"></el-table-column>
+      <el-table-column prop="status" label="完成状态"></el-table-column>
     </el-table>
     <div class="part-page">
       <el-pagination
@@ -133,22 +134,17 @@
 </template>
 
 <script>
+import { progressDetailList } from "@/api/invigilation";
+
 export default {
-  name: "ProgressDetail",
+  name: "progress-detail",
   data() {
     return {
       filter: {
-        batchId: null,
-        examroom: null,
-        subjectId: null,
-        auditStatus: null,
-        content: "",
-        strangePersonUp: null,
-        strangePersonDown: null,
-        exceptionUp: null,
-        exceptionDown: null,
-        warningUp: null,
-        warningDown: null,
+        examId: null,
+        roomCode: null,
+        courseCode: null,
+        name: "",
       },
       current: 1,
       total: 0,
@@ -156,36 +152,33 @@ export default {
       batchs: [],
       exams: [],
       subjects: [],
-      dataList: [
-        {
-          id: 1,
-          batchName: "第一批次",
-          examName: "第一场次",
-          examroom: "第一考场",
-          examId: "123456",
-          stdCardNo: "000000000000000008",
-          stdName: "张龙龙",
-          subjectName: "大学英语",
-          subjectCode: "10006",
-          strangeNumber: "0",
-          exceptionNumber: "2",
-          warningNumber: "2",
-          isDiscipline: "",
-          auditStatus: "未阅",
-        },
-      ],
+      dataList: [],
     };
   },
   methods: {
-    getList() {},
-    toPage() {},
-    cleanUnread() {},
-    batchAction() {},
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current - 1,
+        pageSize: this.size,
+      };
+
+      const res = await progressDetailList(datas);
+
+      this.dataList = res.data.data.records;
+      this.total = res.data.data.records.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
     toDetail(row) {
       console.log(row);
       this.$router.push({ name: "WainingDetail", params: { id: row.id } });
     },
-    toExport() {},
+    toExport() {
+      // TODO:
+    },
   },
 };
 </script>

+ 68 - 0
src/features/invigilation/RealtimeMonitoring/ExamBatchDialog.vue

@@ -0,0 +1,68 @@
+<template>
+  <el-dialog
+    class="exam-batch-dialog"
+    :visible.sync="dialogVisible"
+    width="600px"
+    title="请选择考试批次"
+    :show-close="false"
+    :close-on-press-escape="false"
+    :close-on-click-modal="false"
+    append-to-body
+  >
+    <el-radio-group size="medium" v-model="examBatchId">
+      <el-radio v-for="item in examBatchs" :key="item.id" :label="item.id">{{
+        item.label
+      }}</el-radio>
+    </el-radio-group>
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="cancel" plain>取消</el-button>
+      <el-button type="primary" @click="confirm">确认</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { examBatchList } from "@/api/invigilation";
+
+export default {
+  name: "exam-batch-dialog",
+  data() {
+    return {
+      dialogVisible: false,
+      examBatchId: null,
+      examBatchs: [],
+    };
+  },
+  mounted() {
+    this.getExamList();
+  },
+  methods: {
+    async getExamList() {
+      const res = await examBatchList({ pageNumber: 0, pageSize: 100 });
+      this.examBatchs = res.data.data.map((item) => {
+        return {
+          ...item,
+          label: `${item.name}【${item.startTime} - ${item.endTime}】`,
+        };
+      });
+
+      this.$emit("confirm", this.examBatchs[0]);
+    },
+    cancel() {
+      this.dialogVisible = false;
+    },
+    open() {
+      this.dialogVisible = true;
+    },
+    confirm() {
+      if (!this.examBatchId) {
+        this.$message.error("请选择考试批次!");
+        return;
+      }
+      const exam = this.examBatchs.find((item) => item.id === this.examBatchId);
+      this.$emit("confirm", exam);
+      this.cancel();
+    },
+  },
+};
+</script>

+ 214 - 44
src/features/invigilation/RealtimeMonitoring/RealtimeMonitoring.vue

@@ -2,19 +2,27 @@
   <div class="realtime-monitoring">
     <div class="realtime-top clear-float">
       <p v-if="examName">考场名称:{{ examName }}</p>
-      <el-select
-        v-model="filter.examId"
-        placeholder="请选择批次"
-        @change="examChange"
-        clearable
+      <div
+        class="el-select el-select--small"
+        @click="$refs.ExamBatchDialog.open()"
       >
-        <el-option
-          v-for="item in exams"
-          :key="item.id"
-          :value="item.id"
-          :label="item.name"
-        ></el-option>
-      </el-select>
+        <div class="el-input el-input--small el-input--suffix">
+          <input
+            type="text"
+            readonly="readonly"
+            autocomplete="off"
+            placeholder="请选择批次"
+            v-model="curExamBatch.label"
+            class="el-input__inner"
+          />
+          <span class="el-input__suffix">
+            <span class="el-input__suffix-inner"
+              ><i
+                class="el-select__caret el-input__icon el-icon-arrow-up"
+              ></i></span
+          ></span>
+        </div>
+      </div>
       <p>现在是2020年6月2日 星期二 上午 09:30:12</p>
     </div>
 
@@ -23,7 +31,11 @@
         <h1>实时监控台</h1>
       </div>
       <div class="part-box-head-right">
-        <el-radio-group size="small" v-model="pageType">
+        <el-radio-group
+          size="small"
+          v-model="pageType"
+          @change="pageTypeChange"
+        >
           <el-radio-button label="0">
             <i class="el-icon-s-fold"></i>列表</el-radio-button
           >
@@ -38,20 +50,52 @@
       <div class="part-filter-info">
         <div class="part-filter-info-main summary-line">
           <p class="summary-line-item">
-            <i class="icon icon-users"></i
-            ><span class="line-name">全部应考</span><span>50人</span>
+            <i class="icon icon-users"></i>
+            <el-popover
+              placement="top-start"
+              width="200"
+              trigger="hover"
+              content="全部应考:参加考试的全部考生。"
+            >
+              <span class="line-name" slot="reference">全部应考</span>
+            </el-popover>
+            <span>{{ examPropData.allCount }}人</span>
           </p>
           <p class="summary-line-item">
-            <i class="line-point line-point-info"></i
-            ><span class="line-name">已登录</span><span>5人</span>
+            <i class="line-point line-point-info"></i>
+            <el-popover
+              placement="top-start"
+              width="200"
+              trigger="hover"
+              content="已登录:已成功登录考生端的考生。"
+            >
+              <span class="line-name" slot="reference">已登录</span>
+            </el-popover>
+            <span>{{ examPropData.loginCount }}人</span>
           </p>
           <p class="summary-line-item">
-            <i class="line-point line-point-success"></i
-            ><span class="line-name">已待考</span><span>3人</span>
+            <i class="line-point line-point-success"></i>
+            <el-popover
+              placement="top-start"
+              width="200"
+              trigger="hover"
+              content="已待考:已进入待考界面等待开考的考生。"
+            >
+              <span class="line-name" slot="reference">已待考</span>
+            </el-popover>
+            <span>{{ examPropData.prepareCount }}人</span>
           </p>
           <p class="summary-line-item">
-            <i class="line-point line-point-primary"></i
-            ><span class="line-name">考试中</span><span>2人</span>
+            <i class="line-point line-point-primary"></i>
+            <el-popover
+              placement="top-start"
+              width="200"
+              trigger="hover"
+              content="考试中:正在答题的考生。"
+            >
+              <span class="line-name" slot="reference">考试中</span>
+            </el-popover>
+            <span>{{ examPropData.notComplete }}人</span>
           </p>
           <p class="summary-line-item">
             <i class="line-point line-point-danger"></i>
@@ -59,7 +103,7 @@
               placement="top-start"
               width="200"
               trigger="hover"
-              content="这是一段内容,这是一段内容,这是一段内容,这是一段内容。"
+              content="通讯故障:考生端出现断网、断电、软硬件故障等异常导致考生端与监考端无法正常连接的考生。"
             >
               <span class="line-name" slot="reference">通讯故障</span>
             </el-popover>
@@ -144,17 +188,47 @@
         </el-form>
 
         <div class="part-filter-form-action">
-          <el-button type="primary" icon="icon icon-handle" @click="cleanUnread"
+          <el-dropdown
+            @command="viewingAngleChange"
+            style="margin-right: 10px;"
+            v-if="pageType === '1'"
+          >
+            <el-button type="primary"
+              >{{ curViewingAngle.name || "切换视角"
+              }}<i class="el-icon-arrow-down el-icon--right"></i
+            ></el-button>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item
+                v-for="item in viewingAngles"
+                :key="item.code"
+                :command="item"
+                >切换{{ item.name }}</el-dropdown-item
+              >
+            </el-dropdown-menu>
+          </el-dropdown>
+          <el-button
+            type="primary"
+            icon="icon icon-handle"
+            @click="finishInvigilation"
             >手动收卷</el-button
           >
-          <el-button type="danger" icon="icon icon-over" @click="batchAction"
+          <el-button
+            type="danger"
+            icon="icon icon-over"
+            @click="finishInvigilationExam"
             >结束监考</el-button
           >
         </div>
       </div>
     </div>
 
-    <el-table ref="TableList" :data="dataList">
+    <el-table
+      ref="TableList"
+      :data="dataList"
+      @selection-change="handleSelectionChange"
+      v-if="pageType === '0'"
+    >
+      <el-table-column type="selection" width="55"> </el-table-column>
       <el-table-column prop="identity" label="证件号"></el-table-column>
       <el-table-column prop="name" label="姓名"></el-table-column>
       <el-table-column prop="courseName" label="科目名称"></el-table-column>
@@ -184,6 +258,16 @@
         </template>
       </el-table-column>
     </el-table>
+    <div class="invigilation-student-list" v-else>
+      <div
+        class="invigilation-student-item"
+        v-for="item in dataList"
+        :key="item.examStudentId"
+      >
+        <invigilation-student :data="item"></invigilation-student>
+      </div>
+    </div>
+
     <div class="part-page">
       <el-pagination
         background
@@ -195,14 +279,28 @@
       >
       </el-pagination>
     </div>
+
+    <exam-batch-dialog
+      @confirm="examChange"
+      ref="ExamBatchDialog"
+    ></exam-batch-dialog>
   </div>
 </template>
 
 <script>
-import { invigilateList, examList } from "@/api/invigilation";
+import {
+  invigilateList,
+  invigilateVideoList,
+  invigilateFinish,
+  invigilateExamFinish,
+  examPropCount,
+} from "@/api/invigilation";
+import ExamBatchDialog from "./ExamBatchDialog";
+import InvigilationStudent from "../common/InvigilationStudent";
 
 export default {
   name: "realtime-monitoring",
+  components: { ExamBatchDialog, InvigilationStudent },
   data() {
     return {
       filter: {
@@ -215,34 +313,51 @@ export default {
         minWarningCount: undefined,
       },
       examName: "",
+      curExamBatch: {},
+      curViewingAngle: {},
+      examPropData: {},
       current: 1,
       total: 0,
       size: 10,
+      multipleSelection: [],
       batchId: "",
       batchs: [],
       exams: [],
       subjects: [],
       pageType: "0",
       dataList: [],
+      viewingAngles: [
+        {
+          code: "1",
+          name: "第一视角",
+        },
+        {
+          code: "2",
+          name: "第二视角",
+        },
+        {
+          code: "3",
+          name: "第三视角",
+        },
+      ],
     };
   },
-  mounted() {
-    this.initData();
-  },
+  mounted() {},
   methods: {
-    async initData() {
-      await this.getExamList();
-      this.filter.examId = this.exams[0] && this.exams[0].id;
-      this.examChange();
-      this.getList();
-    },
     async getList() {
       const datas = {
         ...this.filter,
         pageNumber: this.current - 1,
         pageSize: this.size,
       };
-      const res = await invigilateList(datas);
+
+      let res = null;
+      if (this.pageType === "0") {
+        res = await invigilateList(datas);
+      } else {
+        res = await invigilateVideoList(datas);
+      }
+
       this.dataList = res.data.data.records;
       this.total = res.data.data.records.total;
     },
@@ -250,16 +365,57 @@ export default {
       this.current = page;
       this.getList();
     },
-    examChange() {
-      const exam = this.exams.find((item) => item.id === this.filter.examId);
-      this.examName = exam.name;
+    async getExamPropCount() {
+      this.examPropData = await examPropCount(this.filter.examId);
+    },
+    examChange(examBatch) {
+      if (!examBatch) return;
+      this.filter.examId = examBatch.id;
+      this.curExamBatch = examBatch;
+      this.toPage(1);
     },
-    async getExamList() {
-      const res = await examList({ pageNumber: 0, pageSize: 100 });
-      this.exams = res.data.data.records;
+    pageTypeChange() {
+      this.toPage(1);
+    },
+    handleSelectionChange(val) {
+      console.log(val);
+      this.multipleSelection = val;
+    },
+    viewingAngleChange(data) {
+      this.curViewingAngle = data;
+    },
+    async finishInvigilation() {
+      const result = await this.$confirm("确定要手动收卷吗?", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "confirm",
+      }).catch(() => {});
+
+      if (!result) return;
+
+      await invigilateFinish({ examRecordId: "", type: false });
+      this.toPage(1);
+      this.$message({
+        type: "success",
+        message: "操作成功!",
+      });
+    },
+    async finishInvigilationExam() {
+      const result = await this.$confirm("确定要结束监考吗?", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "confirm",
+      }).catch(() => {});
+
+      if (!result) return;
+
+      await invigilateExamFinish();
+      this.toPage(1);
+      this.$message({
+        type: "success",
+        message: "操作成功!",
+      });
     },
-    cleanUnread() {},
-    batchAction() {},
     toCommunication() {
       this.$router.push({ name: "VideoCommunication" });
     },
@@ -309,4 +465,18 @@ export default {
     opacity: 0.8;
   }
 }
+.invigilation-student-list {
+  background: #ffffff;
+  border-radius: 6px;
+  padding: 10px 10px;
+  font-size: 0;
+  min-height: 200px;
+  .invigilation-student-item {
+    font-size: 14px;
+    display: inline-block;
+    vertical-align: top;
+    padding: 10px;
+    width: 25%;
+  }
+}
 </style>

+ 1 - 1
src/features/invigilation/RealtimeMonitoring/WainingDetail.vue

@@ -162,7 +162,7 @@
 
 <script>
 import { createClient, createStream } from "@/plugins/trtc";
-import FlvMedia from "./FlvMedia";
+import FlvMedia from "../common/FlvMedia";
 import { invigilateDetail, warningStudentDetail } from "@/api/invigilation";
 
 export default {

+ 165 - 0
src/features/invigilation/ReexamApply/ApplyReexamDialog.vue

@@ -0,0 +1,165 @@
+<template>
+  <el-dialog
+    class="apply-reexam-dialog"
+    :visible.sync="dialogVisible"
+    width="640px"
+    title="设置重考"
+    :close-on-press-escape="false"
+    :close-on-click-modal="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      label-width="100px"
+    >
+      <el-form-item prop="model" label="重考方式:">
+        <el-select v-model="modalForm.model" placeholder="请选择" clearable>
+          <el-option
+            v-for="(val, key) in REEXAM_TYPE"
+            :key="key"
+            :value="key"
+            :label="val"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item prop="examRecordId" label="证件号/姓名:">
+        <el-select
+          v-model="modalForm.examRecordId"
+          placeholder="请选择"
+          :multiple="students.length > 1"
+          :readonly="students.length === 1"
+          clearable
+        >
+          <el-option
+            v-for="item in students"
+            :key="item.examRecordId"
+            :value="item.examRecordId"
+            :label="item.label"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item prop="reason" label="重考原因:">
+        <el-select v-model="modalForm.reason" placeholder="请选择" clearable>
+          <el-option
+            v-for="(val, key) in REEXAM_REASON"
+            :key="key"
+            :value="key"
+            :label="val"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="备注详情:">
+        <el-input
+          type="textarea"
+          v-model.trim="modalForm.remark"
+          placeholder="请输入"
+          :maxlength="200"
+          show-word-limit
+          clearable
+        ></el-input>
+      </el-form-item>
+    </el-form>
+
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="cancel" plain>取消</el-button>
+      <el-button type="primary" @click="confirm">确认</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { applyReexam } from "@/api/invigilation";
+import { REEXAM_TYPE, REEXAM_REASON } from "@/constant/constants";
+
+const initModalForm = {
+  examRecordId: [],
+  model: 0,
+  reason: "",
+  remark: "",
+};
+
+export default {
+  name: "apply-reexam-dialog",
+  props: {
+    students: {
+      type: Array,
+      default() {
+        return [];
+      },
+    },
+  },
+  data() {
+    const recordIdValidator = (rule, value, callback) => {
+      if (!value || !value.length) {
+        callback(new Error("请选择学生"));
+      } else {
+        callback();
+      }
+    };
+    return {
+      dialogVisible: false,
+      isSubmit: false,
+      modalForm: { ...initModalForm },
+      REEXAM_TYPE,
+      REEXAM_REASON,
+      rules: {
+        model: [
+          {
+            required: true,
+            message: "请选择重考方式",
+            trigger: "change",
+          },
+        ],
+        examRecordId: [
+          {
+            required: true,
+            validator: recordIdValidator,
+            trigger: "change",
+          },
+        ],
+        reason: [
+          {
+            required: true,
+            message: "请选择重考原因",
+            trigger: "change",
+          },
+        ],
+      },
+    };
+  },
+  methods: {
+    visibleChange() {
+      this.modalForm = { ...initModalForm };
+      this.modalForm.examRecordId = this.students.map(
+        (item) => item.examRecordId
+      );
+    },
+    cancel() {
+      this.dialogVisible = false;
+    },
+    open() {
+      this.dialogVisible = true;
+    },
+    async confirm() {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      const data = await applyReexam(this.modalForm).catch(() => {
+        this.isSubmit = false;
+      });
+
+      if (!data) return;
+
+      this.isSubmit = false;
+      this.$message.success("操作成功!");
+      this.$emit("modified");
+      this.cancel();
+    },
+  },
+};
+</script>

+ 65 - 52
src/features/invigilation/ReexamApply/ReexamApply.vue

@@ -9,7 +9,7 @@
         <el-form ref="FilterForm" label-position="left" inline>
           <el-form-item>
             <el-select
-              v-model="filter.batchId"
+              v-model="filter.examId"
               placeholder="请选择批次"
               clearable
             >
@@ -23,7 +23,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.examroom"
+              v-model="filter.roomCode"
               placeholder="请选择考场"
               clearable
             >
@@ -37,7 +37,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.courseCode"
               placeholder="请选择科目"
               clearable
             >
@@ -51,7 +51,7 @@
           </el-form-item>
           <el-form-item>
             <el-input
-              v-model.trim="filter.content"
+              v-model.trim="filter.name"
               placeholder="姓名/证件号"
               clearable
             ></el-input>
@@ -62,7 +62,7 @@
         </el-form>
 
         <div class="part-filter-form-action">
-          <el-button type="primary" icon="el-icon-s-tools" @click="batchAction"
+          <el-button type="primary" icon="icon icon-seal" @click="batchAction"
             >批量设置重考</el-button
           >
         </div>
@@ -73,25 +73,24 @@
       ref="TableList"
       :data="dataList"
       @selection-change="handleSelectionChange"
-      border
-      stripe
     >
       <el-table-column type="selection" width="55"> </el-table-column>
-      <el-table-column
-        prop="batchName"
-        label="考场名称(代码)"
-      ></el-table-column>
-      <el-table-column prop="stdCardNo" label="证件号"></el-table-column>
-      <el-table-column prop="stdName" label="姓名"></el-table-column>
-      <el-table-column prop="subjectName" label="科目名称"></el-table-column>
-      <el-table-column prop="subjectCode" label="科目(代码)"></el-table-column>
+      <el-table-column prop="roomName" label="考场名称(代码)">
+        <span slot-scope="scope"
+          >{{ scope.row.roomName }}({{ scope.row.roomCode }})</span
+        >
+      </el-table-column>
+      <el-table-column prop="identity" label="证件号"></el-table-column>
+      <el-table-column prop="name" label="姓名"></el-table-column>
+      <el-table-column prop="courseName" label="科目名称"></el-table-column>
+      <el-table-column prop="courseCode" label="科目代码"></el-table-column>
       <el-table-column label="操作">
         <template slot-scope="scope">
           <el-button
             class="btn-table-icon"
             type="primary"
-            icon="el-icon-s-tools"
-            @click="toDetail(scope.row)"
+            icon="icon icon-seal"
+            @click="toEdit(scope.row)"
             >重考</el-button
           >
         </template>
@@ -108,26 +107,29 @@
       >
       </el-pagination>
     </div>
+
+    <apply-reexam-dialog
+      :students="selectStudents"
+      @modified="applyFinish"
+      ref="ApplyReexamDialog"
+    ></apply-reexam-dialog>
   </div>
 </template>
 
 <script>
+import { reexamApplyList } from "@/api/invigilation";
+import ApplyReexamDialog from "./ApplyReexamDialog";
+
 export default {
   name: "ReexamApply",
+  components: { ApplyReexamDialog },
   data() {
     return {
       filter: {
-        batchId: null,
+        examId: null,
         examroom: null,
-        subjectId: null,
-        auditStatus: null,
-        content: "",
-        strangePersonUp: null,
-        strangePersonDown: null,
-        exceptionUp: null,
-        exceptionDown: null,
-        warningUp: null,
-        warningDown: null,
+        courseCode: null,
+        name: "",
       },
       current: 1,
       total: 0,
@@ -135,38 +137,49 @@ export default {
       batchs: [],
       exams: [],
       subjects: [],
-      dataList: [
-        {
-          id: 1,
-          batchName: "第一批次",
-          examName: "第一场次",
-          examroom: "第一考场",
-          examId: "123456",
-          stdCardNo: "000000000000000008",
-          stdName: "张龙龙",
-          subjectName: "大学英语",
-          subjectCode: "10006",
-          strangeNumber: "0",
-          exceptionNumber: "2",
-          warningNumber: "2",
-          isDiscipline: "",
-          auditStatus: "未阅",
-        },
-      ],
+      dataList: [],
+      selectStudents: [],
       multipleSelection: [],
     };
   },
   methods: {
-    getList() {},
-    toPage() {},
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current - 1,
+        pageSize: this.size,
+      };
+
+      const res = await reexamApplyList(datas);
+
+      this.dataList = res.data.data.records.map((item) => {
+        item.label = `${item.identity} ${item.courseName}(${item.courseCode}) ${item.name}`;
+      });
+      this.total = res.data.data.records.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
     handleSelectionChange(val) {
       this.multipleSelection = val;
     },
-    cleanUnread() {},
-    batchAction() {},
-    toDetail(row) {
-      console.log(row);
-      this.$router.push({ name: "WainingDetail", params: { id: row.id } });
+    batchAction() {
+      if (!this.multipleSelection.length) {
+        this.$message.error("请先选择数据!");
+        return;
+      }
+      this.selectStudents = this.multipleSelection;
+      this.$refs.ApplyReexamDialog.open();
+    },
+    toEdit(row) {
+      this.selectStudents = [row];
+      this.$refs.ApplyReexamDialog.open();
+    },
+    applyFinish() {
+      this.selectStudents = [];
+      this.multipleSelection = [];
+      this.getList();
     },
   },
 };

+ 79 - 59
src/features/invigilation/ReexamChecked/ReexamChecked.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="reexam-checked">
+  <div class="reexam-pending">
     <div class="part-box-head">
       <div class="part-box-head-left"><h1>重考已审</h1></div>
     </div>
@@ -8,7 +8,7 @@
         <el-form ref="FilterForm" label-position="left" inline>
           <el-form-item>
             <el-select
-              v-model="filter.batchId"
+              v-model="filter.examId"
               placeholder="请选择原批次"
               clearable
             >
@@ -22,7 +22,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.examroom"
+              v-model="filter.roomCode"
               placeholder="请选择考场"
               clearable
             >
@@ -36,7 +36,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.courseCode"
               placeholder="请选择科目"
               clearable
             >
@@ -50,21 +50,21 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.reason"
               placeholder="请选择申请理由"
               clearable
             >
               <el-option
-                v-for="item in subjects"
-                :key="item.id"
-                :value="item.id"
-                :label="item.name"
+                v-for="(val, key) in REEXAM_REASON"
+                :key="key"
+                :value="key"
+                :label="val"
               ></el-option>
             </el-select>
           </el-form-item>
           <el-form-item>
             <el-input
-              v-model.trim="filter.content"
+              v-model.trim="filter.name"
               placeholder="姓名/证件号"
               clearable
             ></el-input>
@@ -73,15 +73,18 @@
             <el-date-picker
               v-model="applyTime"
               type="daterange"
+              @change="applyTimeChange"
               range-separator="至"
               start-placeholder="开始日期"
               end-placeholder="结束日期"
+              value-format="yyyy-MM-dd"
+              format="yyyy-MM-dd"
             >
             </el-date-picker>
           </el-form-item>
           <el-form-item>
             <el-input
-              v-model.trim="filter.content"
+              v-model.trim="filter.applyName"
               placeholder="申请人姓名"
               clearable
             ></el-input>
@@ -95,25 +98,29 @@
     </div>
 
     <el-table ref="TableList" :data="dataList">
-      <el-table-column prop="examroom" label="考场(代码)"> </el-table-column>
-      <el-table-column prop="stdCardNo" label="证件号"></el-table-column>
-      <el-table-column prop="stdCardNo" label="科目名称"></el-table-column>
-      <el-table-column prop="stdName" label="姓名"></el-table-column>
-      <el-table-column prop="subjectCode" label="科目(代码)"></el-table-column>
-      <el-table-column prop="subjectCode" label="原批次"></el-table-column>
-      <el-table-column prop="subjectCode" label="原场次"></el-table-column>
-      <el-table-column prop="subjectCode" label="申请理由"></el-table-column>
-      <el-table-column prop="subjectCode" label="申请时间"></el-table-column>
-      <el-table-column prop="subjectCode" label="申请人"></el-table-column>
-      <el-table-column prop="subjectCode" label="审核时间"></el-table-column>
-      <el-table-column prop="subjectCode" label="审核结果"></el-table-column>
-      <el-table-column prop="subjectCode" label="审核人"></el-table-column>
+      <el-table-column prop="examroom" label="考场(代码)">
+        <span slot-scope="scope"
+          >{{ scope.row.roomName }}({{ scope.row.roomCode }})</span
+        >
+      </el-table-column>
+      <el-table-column prop="identity" label="证件号"></el-table-column>
+      <el-table-column prop="courseName" label="科目名称"></el-table-column>
+      <el-table-column prop="name" label="姓名"></el-table-column>
+      <el-table-column prop="courseCode" label="科目代码"></el-table-column>
+      <el-table-column prop="orgExamName" label="原批次"></el-table-column>
+      <el-table-column prop="orgRoomName" label="原场次"></el-table-column>
+      <el-table-column prop="reasom" label="申请理由"></el-table-column>
+      <el-table-column prop="reasonTime" label="申请时间"></el-table-column>
+      <el-table-column prop="reasonUser" label="申请人"></el-table-column>
+      <el-table-column prop="auditingTime" label="审核时间"></el-table-column>
+      <el-table-column prop="status" label="审核结果"></el-table-column>
+      <el-table-column prop="auditingId" label="审核人"></el-table-column>
       <el-table-column label="操作">
         <template slot-scope="scope">
           <el-button
             class="btn-table-icon"
             type="primary"
-            icon="icon icon-view"
+            icon="icon icon-seal"
             @click="toDetail(scope.row)"
             >详情</el-button
           >
@@ -131,27 +138,35 @@
       >
       </el-pagination>
     </div>
+
+    <check-reexam-dialog
+      :instance="curReexam"
+      @modified="getList"
+      ref="CheckReexamDialog"
+    ></check-reexam-dialog>
   </div>
 </template>
 
 <script>
+import { reexamCheckedList } from "@/api/invigilation";
+import { REEXAM_REASON } from "@/constant/constants";
+import CheckReexamDialog from "../ReexamPending/CheckReexamDialog";
+
 export default {
-  name: "reexam-checked",
+  name: "reexam-pending",
+  components: { CheckReexamDialog },
   data() {
     return {
       filter: {
-        batchId: null,
+        examId: null,
         examroom: null,
-        subjectId: null,
-        auditStatus: null,
-        content: "",
-        strangePersonUp: null,
-        strangePersonDown: null,
-        exceptionUp: null,
-        exceptionDown: null,
-        warningUp: null,
-        warningDown: null,
+        courseCode: null,
+        name: "",
+        applyName: "",
+        reasonStartTime: "",
+        reasonEndTime: "",
       },
+      REEXAM_REASON,
       current: 1,
       total: 0,
       size: 10,
@@ -159,34 +174,39 @@ export default {
       exams: [],
       subjects: [],
       applyTime: "",
-      dataList: [
-        {
-          id: 1,
-          batchName: "第一批次",
-          examName: "第一场次",
-          examroom: "第一考场",
-          examId: "123456",
-          stdCardNo: "000000000000000008",
-          stdName: "张龙龙",
-          subjectName: "大学英语",
-          subjectCode: "10006",
-          strangeNumber: "0",
-          exceptionNumber: "2",
-          warningNumber: "2",
-          isDiscipline: "",
-          auditStatus: "未阅",
-        },
-      ],
+      dataList: [],
+      curReexam: {},
+      multipleSelection: [],
     };
   },
   methods: {
-    getList() {},
-    toPage() {},
-    cleanUnread() {},
-    batchAction() {},
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current - 1,
+        pageSize: this.size,
+      };
+
+      const res = await reexamCheckedList(datas);
+
+      this.dataList = res.data.data.records.map((item) => {
+        item.label = `${item.identity} ${item.courseName}(${item.courseCode}) ${item.name}`;
+      });
+      this.total = res.data.data.records.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    applyTimeChange(vals) {
+      const dvals = vals || [];
+      this.filter.reasonStartTime = dvals[0] || null;
+      this.filter.reasonEndTime = dvals[1] || null;
+    },
     toDetail(row) {
       console.log(row);
-      this.$router.push({ name: "WainingDetail", params: { id: row.id } });
+      this.curReexam = { ...row };
+      this.$refs.CheckReexamDialog.open();
     },
   },
 };

+ 154 - 0
src/features/invigilation/ReexamPending/CheckReexamDialog.vue

@@ -0,0 +1,154 @@
+<template>
+  <el-dialog
+    class="reexam-check-dialog"
+    :visible.sync="dialogVisible"
+    width="640px"
+    :title="title"
+    :close-on-press-escape="false"
+    :close-on-click-modal="false"
+    append-to-body
+    @open="visibleChange"
+  >
+    <el-form
+      ref="modalFormComp"
+      :model="modalForm"
+      :rules="rules"
+      label-width="100px"
+    >
+      <el-form-item prop="reexamId" label="证件号/姓名:">
+        <el-input v-model="modalForm.label" :readonly="!isEdit"></el-input>
+      </el-form-item>
+      <el-form-item prop="reason" label="重考原因:">
+        <el-select
+          v-model="modalForm.reason"
+          placeholder="请选择"
+          :readonly="!isEdit"
+        >
+          <el-option
+            v-for="(val, key) in REEXAM_REASON"
+            :key="key"
+            :value="key"
+            :label="val"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="备注详情:">
+        <el-input
+          type="textarea"
+          v-model.trim="modalForm.remark"
+          placeholder="请输入"
+          :maxlength="200"
+          show-word-limit
+          readonly
+        ></el-input>
+      </el-form-item>
+      <el-form-item label="审批结果:" v-if="!isEdit">
+        <p>{{ !modalForm.auditingStatus ? "同意" : "不同意" }}</p>
+      </el-form-item>
+      <el-form-item label="审批意见:">
+        <el-input
+          type="textarea"
+          v-model.trim="modalForm.auditingSuggest"
+          placeholder="请输入"
+          :maxlength="200"
+          show-word-limit
+          :readonly="!isEdit"
+          :clearable="isEdit"
+        ></el-input>
+      </el-form-item>
+    </el-form>
+
+    <div slot="footer" class="dialog-footer">
+      <el-button type="danger" @click="confirm(0)">不同意</el-button>
+      <el-button type="primary" @click="confirm(1)">同意</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import { checkReexamApply } from "@/api/invigilation";
+import { REEXAM_REASON } from "@/constant/constants";
+
+const initModalForm = {
+  reexamId: "",
+  auditingStatus: 0,
+  auditingSuggest: "",
+  reason: "",
+  remark: "",
+  label: "",
+  status: 0,
+};
+
+export default {
+  name: "reexam-check-dialog",
+  props: {
+    instance: {
+      type: Object,
+      default() {
+        return [];
+      },
+    },
+  },
+  computed: {
+    isEdit() {
+      return !this.instance.status;
+    },
+    title() {
+      return this.isEdit ? "重考审核" : "查看详情";
+    },
+  },
+  data() {
+    return {
+      dialogVisible: false,
+      isSubmit: false,
+      modalForm: { ...initModalForm },
+      REEXAM_REASON,
+      rules: {
+        reexamId: [
+          {
+            required: true,
+            message: "请选择审核记录",
+            trigger: "change",
+          },
+        ],
+        reason: [
+          {
+            required: true,
+            message: "请选择重考原因",
+            trigger: "change",
+          },
+        ],
+      },
+    };
+  },
+  methods: {
+    visibleChange() {
+      this.modalForm = Object.assign({}, initModalForm, this.instance);
+    },
+    cancel() {
+      this.dialogVisible = false;
+    },
+    open() {
+      this.dialogVisible = true;
+    },
+    async confirm(auditingStatus) {
+      const valid = await this.$refs.modalFormComp.validate().catch(() => {});
+      if (!valid) return;
+
+      if (this.isSubmit) return;
+      this.isSubmit = true;
+      this.modalForm.auditingStatus = auditingStatus;
+      const data = await checkReexamApply(this.modalForm).catch(() => {
+        this.isSubmit = false;
+      });
+
+      if (!data) return;
+
+      this.isSubmit = false;
+      this.$message.success("操作成功!");
+      this.$emit("modified");
+      this.cancel();
+    },
+  },
+};
+</script>

+ 85 - 54
src/features/invigilation/ReexamPending/ReexamPending.vue

@@ -8,7 +8,7 @@
         <el-form ref="FilterForm" label-position="left" inline>
           <el-form-item>
             <el-select
-              v-model="filter.batchId"
+              v-model="filter.examId"
               placeholder="请选择原批次"
               clearable
             >
@@ -22,7 +22,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.examroom"
+              v-model="filter.roomCode"
               placeholder="请选择考场"
               clearable
             >
@@ -36,7 +36,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.courseCode"
               placeholder="请选择科目"
               clearable
             >
@@ -50,21 +50,21 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.reason"
               placeholder="请选择申请理由"
               clearable
             >
               <el-option
-                v-for="item in subjects"
-                :key="item.id"
-                :value="item.id"
-                :label="item.name"
+                v-for="(val, key) in REEXAM_REASON"
+                :key="key"
+                :value="key"
+                :label="val"
               ></el-option>
             </el-select>
           </el-form-item>
           <el-form-item>
             <el-input
-              v-model.trim="filter.content"
+              v-model.trim="filter.name"
               placeholder="姓名/证件号"
               clearable
             ></el-input>
@@ -73,15 +73,18 @@
             <el-date-picker
               v-model="applyTime"
               type="daterange"
+              @change="applyTimeChange"
               range-separator="至"
               start-placeholder="开始日期"
               end-placeholder="结束日期"
+              value-format="yyyy-MM-dd"
+              format="yyyy-MM-dd"
             >
             </el-date-picker>
           </el-form-item>
           <el-form-item>
             <el-input
-              v-model.trim="filter.content"
+              v-model.trim="filter.applyName"
               placeholder="申请人姓名"
               clearable
             ></el-input>
@@ -91,26 +94,38 @@
             <el-button type="primary" @click="toPage(1)">查询</el-button>
           </el-form-item>
         </el-form>
+
+        <!-- 暂时不做 -->
+        <!-- <div class="part-filter-form-action">
+          <el-button type="primary" icon="icon icon-seal" @click="batchAction"
+            >批量审核</el-button
+          >
+        </div> -->
       </div>
     </div>
 
     <el-table ref="TableList" :data="dataList">
-      <el-table-column prop="examroom" label="考场(代码)"> </el-table-column>
-      <el-table-column prop="stdCardNo" label="证件号"></el-table-column>
-      <el-table-column prop="stdCardNo" label="科目名称"></el-table-column>
-      <el-table-column prop="stdName" label="姓名"></el-table-column>
-      <el-table-column prop="subjectCode" label="科目(代码)"></el-table-column>
-      <el-table-column prop="subjectCode" label="原批次"></el-table-column>
-      <el-table-column prop="subjectCode" label="原场次"></el-table-column>
-      <el-table-column prop="subjectCode" label="申请理由"></el-table-column>
-      <el-table-column prop="subjectCode" label="申请时间"></el-table-column>
-      <el-table-column prop="subjectCode" label="申请人"></el-table-column>
+      <!-- <el-table-column type="selection" width="55"> </el-table-column> -->
+      <el-table-column prop="examroom" label="考场(代码)">
+        <span slot-scope="scope"
+          >{{ scope.row.roomName }}({{ scope.row.roomCode }})</span
+        >
+      </el-table-column>
+      <el-table-column prop="identity" label="证件号"></el-table-column>
+      <el-table-column prop="courseName" label="科目名称"></el-table-column>
+      <el-table-column prop="name" label="姓名"></el-table-column>
+      <el-table-column prop="courseCode" label="科目代码"></el-table-column>
+      <el-table-column prop="orgExamName" label="原批次"></el-table-column>
+      <el-table-column prop="orgRoomName" label="原场次"></el-table-column>
+      <el-table-column prop="reasom" label="申请理由"></el-table-column>
+      <el-table-column prop="reasonTime" label="申请时间"></el-table-column>
+      <el-table-column prop="reasonUser" label="申请人"></el-table-column>
       <el-table-column label="操作">
         <template slot-scope="scope">
           <el-button
             class="btn-table-icon"
             type="primary"
-            icon="icon icon-view"
+            icon="icon icon-seal"
             @click="toDetail(scope.row)"
             >详情</el-button
           >
@@ -128,27 +143,35 @@
       >
       </el-pagination>
     </div>
+
+    <check-reexam-dialog
+      :instance="curReexam"
+      @modified="getList"
+      ref="CheckReexamDialog"
+    ></check-reexam-dialog>
   </div>
 </template>
 
 <script>
+import { reexamPendingList } from "@/api/invigilation";
+import { REEXAM_REASON } from "@/constant/constants";
+import CheckReexamDialog from "./CheckReexamDialog";
+
 export default {
   name: "reexam-pending",
+  components: { CheckReexamDialog },
   data() {
     return {
       filter: {
-        batchId: null,
+        examId: null,
         examroom: null,
-        subjectId: null,
-        auditStatus: null,
-        content: "",
-        strangePersonUp: null,
-        strangePersonDown: null,
-        exceptionUp: null,
-        exceptionDown: null,
-        warningUp: null,
-        warningDown: null,
+        courseCode: null,
+        name: "",
+        applyName: "",
+        reasonStartTime: "",
+        reasonEndTime: "",
       },
+      REEXAM_REASON,
       current: 1,
       total: 0,
       size: 10,
@@ -156,34 +179,42 @@ export default {
       exams: [],
       subjects: [],
       applyTime: "",
-      dataList: [
-        {
-          id: 1,
-          batchName: "第一批次",
-          examName: "第一场次",
-          examroom: "第一考场",
-          examId: "123456",
-          stdCardNo: "000000000000000008",
-          stdName: "张龙龙",
-          subjectName: "大学英语",
-          subjectCode: "10006",
-          strangeNumber: "0",
-          exceptionNumber: "2",
-          warningNumber: "2",
-          isDiscipline: "",
-          auditStatus: "未阅",
-        },
-      ],
+      dataList: [],
+      curReexam: {},
+      multipleSelection: [],
     };
   },
   methods: {
-    getList() {},
-    toPage() {},
-    cleanUnread() {},
-    batchAction() {},
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current - 1,
+        pageSize: this.size,
+      };
+
+      const res = await reexamPendingList(datas);
+
+      this.dataList = res.data.data.records.map((item) => {
+        item.label = `${item.identity} ${item.courseName}(${item.courseCode}) ${item.name}`;
+      });
+      this.total = res.data.data.records.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    applyTimeChange(vals) {
+      const dvals = vals || [];
+      this.filter.reasonStartTime = dvals[0] || null;
+      this.filter.reasonEndTime = dvals[1] || null;
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
     toDetail(row) {
       console.log(row);
-      this.$router.push({ name: "WainingDetail", params: { id: row.id } });
+      this.curReexam = { ...row };
+      this.$refs.CheckReexamDialog.open();
     },
   },
 };

+ 89 - 58
src/features/invigilation/WainingManage/WainingManage.vue

@@ -11,7 +11,7 @@
         <el-form ref="FilterForm" label-position="left" inline>
           <el-form-item>
             <el-select
-              v-model="filter.batchId"
+              v-model="filter.examId"
               placeholder="请选择批次"
               clearable
             >
@@ -25,7 +25,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.examroom"
+              v-model="filter.roomCode"
               placeholder="请选择考场"
               clearable
             >
@@ -39,7 +39,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.subjectId"
+              v-model="filter.courseCode"
               placeholder="请选择科目"
               clearable
             >
@@ -53,7 +53,7 @@
           </el-form-item>
           <el-form-item>
             <el-select
-              v-model="filter.auditStatus"
+              v-model="filter.approveStatus"
               placeholder="审阅状态"
               clearable
             >
@@ -67,7 +67,7 @@
           </el-form-item>
           <el-form-item>
             <el-input
-              v-model.trim="filter.content"
+              v-model.trim="filter.name"
               placeholder="姓名/证件号"
               clearable
             ></el-input>
@@ -75,14 +75,14 @@
           <el-form-item label="陌生人脸">
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.strangePersonDown"
+              v-model.trim="filter.minMultipleFaceCount"
               placeholder="下限"
               :controls="false"
             ></el-input-number>
             <span class="line-split">-</span>
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.strangePersonUp"
+              v-model.trim="filter.maxMultipleFaceCount"
               placeholder="上限"
               :controls="false"
             ></el-input-number>
@@ -90,14 +90,14 @@
           <el-form-item label="异常处理">
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.exceptionDown"
+              v-model.trim="filter.minExceptionCount"
               placeholder="下限"
               :controls="false"
             ></el-input-number>
             <span class="line-split">-</span>
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.exceptionUp"
+              v-model.trim="filter.maxExceptionCount"
               placeholder="上限"
               :controls="false"
             ></el-input-number>
@@ -105,14 +105,14 @@
           <el-form-item label="预警数">
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.warningDown"
+              v-model.trim="filter.minWarningCount"
               placeholder="下限"
               :controls="false"
             ></el-input-number>
             <span class="line-split">-</span>
             <el-input-number
               style="width: 52px;"
-              v-model.trim="filter.warningUp"
+              v-model.trim="filter.maxWarningCount"
               placeholder="上限"
               :controls="false"
             ></el-input-number>
@@ -132,23 +132,32 @@
       </div>
     </div>
 
-    <el-table ref="TableList" :data="dataList">
-      <el-table-column prop="batchName" label="批次"></el-table-column>
+    <el-table
+      ref="TableList"
+      :data="dataList"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="42" />
+      <el-table-column prop="examId" label="批次"></el-table-column>
       <el-table-column prop="examName" label="场次"></el-table-column>
-      <el-table-column prop="examroom" label="考场"> </el-table-column>
+      <el-table-column prop="roomName" label="考场"> </el-table-column>
       <el-table-column prop="examId" label="考试ID"></el-table-column>
-      <el-table-column prop="stdCardNo" label="证件号"></el-table-column>
-      <el-table-column prop="stdName" label="姓名"></el-table-column>
+      <el-table-column prop="identity" label="证件号"></el-table-column>
+      <el-table-column prop="name" label="姓名"></el-table-column>
       <el-table-column prop="subjectName" label="科目名称"></el-table-column>
-      <el-table-column prop="subjectCode" label="科目(代码)"></el-table-column>
-      <el-table-column prop="strangeNumber" label="陌生人脸"></el-table-column>
+      <el-table-column prop="courseName" label="科目(代码)">
+        <span slot-scope="scope"
+          >{{ scope.row.courseName }}({{ scope.row.courseCode }})</span
+        >
+      </el-table-column>
       <el-table-column
-        prop="exceptionNumber"
-        label="异常处理"
+        prop="multipleFaceCount"
+        label="陌生人脸"
       ></el-table-column>
-      <el-table-column prop="warningNumber" label="预警数"></el-table-column>
-      <el-table-column prop="isDiscipline" label="是否违纪"></el-table-column>
-      <el-table-column prop="auditStatus" label="审阅状态"></el-table-column>
+      <el-table-column prop="exceptionCount" label="异常处理"></el-table-column>
+      <el-table-column prop="warningCount" label="预警数"></el-table-column>
+      <el-table-column prop="breachStatus" label="是否违纪"></el-table-column>
+      <el-table-column prop="approveStatus" label="审阅状态"></el-table-column>
       <el-table-column label="操作">
         <template slot-scope="scope">
           <el-button
@@ -176,57 +185,79 @@
 </template>
 
 <script>
+import {
+  invigilationWarningList,
+  batchInvigilation,
+  clearInvigilationUnreadWarningList,
+} from "@/api/invigilation";
+
 export default {
   name: "WainingManage",
   data() {
     return {
       filter: {
-        batchId: null,
-        examroom: null,
-        subjectId: null,
-        auditStatus: null,
-        content: "",
-        strangePersonUp: null,
-        strangePersonDown: null,
-        exceptionUp: null,
-        exceptionDown: null,
-        warningUp: null,
-        warningDown: null,
+        examId: null,
+        roomCode: null,
+        courseCode: null,
+        approveStatus: null,
+        name: "",
+        maxMultipleFaceCount: null,
+        minMultipleFaceCount: null,
+        maxExceptionCount: null,
+        minExceptionCount: null,
+        maxWarningCount: null,
+        minWarningCount: null,
       },
+      multipleSelection: [],
       current: 1,
       total: 0,
       size: 10,
       batchs: [],
       exams: [],
       subjects: [],
-      dataList: [
-        {
-          id: 1,
-          batchName: "第一批次",
-          examName: "第一场次",
-          examroom: "第一考场",
-          examId: "123456",
-          stdCardNo: "000000000000000008",
-          stdName: "张龙龙",
-          subjectName: "大学英语",
-          subjectCode: "10006",
-          strangeNumber: "0",
-          exceptionNumber: "2",
-          warningNumber: "2",
-          isDiscipline: "",
-          auditStatus: "未阅",
-        },
-      ],
+      dataList: [],
     };
   },
   methods: {
-    getList() {},
-    toPage() {},
-    cleanUnread() {},
-    batchAction() {},
+    async getList() {
+      const datas = {
+        ...this.filter,
+        pageNumber: this.current - 1,
+        pageSize: this.size,
+      };
+
+      const res = await invigilationWarningList(datas);
+
+      this.dataList = res.data.data.records;
+      this.total = res.data.data.records.total;
+    },
+    toPage(page) {
+      this.current = page;
+      this.getList();
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
+    async cleanUnread() {
+      await clearInvigilationUnreadWarningList();
+      this.$message.success("操作成功!");
+      this.getList();
+    },
+    async batchAction() {
+      if (!this.multipleSelection.length) {
+        this.$message.error("请先选择数据!");
+        return;
+      }
+
+      await batchInvigilation();
+      this.$message.success("操作成功!");
+      this.getList();
+    },
     toDetail(row) {
-      console.log(row);
-      this.$router.push({ name: "WainingDetail", params: { id: row.id } });
+      this.$router.push({
+        name: "WainingDetail",
+        params: { recordId: row.examRecordId },
+      });
     },
   },
 };

+ 1 - 0
src/features/invigilation/common/EchartRender.vue

@@ -184,6 +184,7 @@ export default {
             name: "其他",
             type: "bar",
             data: datas.dataList.map((item) => item.count),
+            barMaxWidth: 35,
             itemStyle: {
               color: "transparent",
             },

+ 5 - 8
src/features/invigilation/RealtimeMonitoring/FlvMedia.vue → src/features/invigilation/common/FlvMedia.vue

@@ -1,7 +1,6 @@
 <template>
   <video
     class="flv-media"
-    :id="data.id"
     ref="VideoMedia"
     @ended="destroyPlayer"
     muted
@@ -14,11 +13,9 @@ import flvjs from "flv.js";
 export default {
   name: "flv-media",
   props: {
-    data: {
-      type: Object,
-      default() {
-        return {};
-      },
+    liveUrl: {
+      type: String,
+      required: true,
     },
   },
   data() {
@@ -31,7 +28,7 @@ export default {
   },
   methods: {
     initVideo() {
-      if (!this.data.src) return;
+      if (!this.liveUrl) return;
       if (!flvjs.isSupported()) return;
 
       this.flvPlayer = flvjs.createPlayer(
@@ -39,7 +36,7 @@ export default {
           type: "flv",
           isLive: true,
           hasAudio: false,
-          url: this.data.src,
+          url: this.liveUrl,
         },
         {
           fixAudioTimestampGap: false,

+ 177 - 0
src/features/invigilation/common/InvigilationStudent.vue

@@ -0,0 +1,177 @@
+<template>
+  <div :class="classes">
+    <div class="student-video">
+      <flv-media
+        ref="ThirdViewVideo"
+        :live-url="data.monitorLiveUrl"
+        v-if="data.monitorLiveUrl"
+      ></flv-media>
+      <div class="student-video-none" v-else>
+        <i class="el-icon-video-camera-solid"></i>
+      </div>
+    </div>
+    <div class="student-info">
+      <h6>
+        <span>{{ data.name }}</span
+        ><i class="icon icon-net-break"></i>
+      </h6>
+      <p>
+        <span>证件号:</span><span>{{ data.identity }}</span>
+      </p>
+      <p><span>答题进度:</span><span>20%</span></p>
+      <div class="student-time">
+        <i class="el-icon-alarm-clock"></i>
+        <span>50:32:15</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import FlvMedia from "./FlvMedia";
+
+export default {
+  name: "invigilation-student",
+  components: { FlvMedia },
+  props: {
+    data: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return {};
+  },
+  computed: {
+    classes() {
+      return [
+        "invigilation-student",
+        {
+          "invigilation-student-warning": this.data.warning,
+        },
+      ];
+    },
+  },
+  methods: {},
+};
+</script>
+
+<style lang="scss" scoped>
+.invigilation-student {
+  padding: 10px;
+  position: relative;
+  margin-bottom: 20px;
+
+  &-warning {
+    &::before {
+      content: "";
+      display: block;
+      position: absolute;
+      top: 0;
+      left: 0;
+      width: 50px;
+      height: 50px;
+      background-image: url(../../../assets/bg-discipline.png);
+      background-size: 100% 100%;
+      z-index: 99;
+    }
+    &::after {
+      content: "";
+      display: block;
+      position: absolute;
+      top: 4px;
+      right: 4px;
+      width: 12px;
+      height: 12px;
+
+      border: 2px solid #fff;
+      border-radius: 50%;
+      background: #fe5863;
+      z-index: 99;
+    }
+  }
+
+  &-netbreak {
+    .icon-net-break {
+      display: inline-block !important;
+    }
+    .student-time {
+      background-color: #fe5863 !important;
+    }
+  }
+
+  &-none {
+    height: 100%;
+    background: #606060;
+    border-radius: 6px;
+    font-size: 50px;
+    text-align: center;
+    padding-top: 90px;
+    color: #202b4b;
+  }
+}
+.student-video {
+  height: 150px;
+  border-radius: 6px;
+  background: #626a82;
+  margin-bottom: 20px;
+  position: relative;
+
+  &::before {
+    content: "";
+    display: block;
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    width: 32px;
+    height: 20px;
+    margin-left: -16px;
+    margin-top: -10px;
+    background-image: url(../../../assets/icon-video.png);
+    background-size: 100% 100%;
+  }
+
+  > video {
+    width: 100%;
+    height: 100%;
+  }
+}
+.student-info {
+  position: relative;
+  padding: 0 10px 10px;
+  .student-time {
+    position: absolute;
+    height: 24px;
+    top: 0;
+    right: 0;
+    padding: 4px 12px;
+    border-radius: 12px;
+    background: #abb8c9;
+    color: #fff;
+    font-size: 12px;
+    > i {
+      margin-right: 5px;
+    }
+  }
+
+  > h6 {
+    font-size: 18px;
+    line-height: 25px;
+    margin-bottom: 8;
+    > .icon {
+      margin-top: -4px;
+      margin-left: 8px;
+      display: none;
+    }
+  }
+  > p {
+    font-size: 14px;
+    font-weight: 400;
+    color: #626a82;
+    line-height: 20px;
+    margin: 0;
+  }
+}
+</style>

+ 0 - 16
src/styles/base.scss

@@ -281,19 +281,3 @@ input:-moz-placeholder {
   }
 }
 // element-custom ------------->
-// el-button
-.el-button {
-  .icon {
-    margin-right: 5px;
-    width: 14px;
-    height: 14px;
-  }
-  .icon-view {
-    height: 10px;
-  }
-  span {
-    display: inline-block;
-    vertical-align: middle;
-    line-height: 1;
-  }
-}

+ 17 - 0
src/styles/element-ui-custom.scss

@@ -23,6 +23,23 @@
 // .el-button
 .el-button {
   border-radius: 6px;
+
+  .icon {
+    margin-right: 5px;
+    width: 13px;
+    height: 13px;
+  }
+  .icon-view {
+    height: 10px;
+  }
+  .icon-upload {
+    height: 12px;
+  }
+  span {
+    display: inline-block;
+    vertical-align: middle;
+    line-height: 1;
+  }
 }
 .el-button--small,
 .el-button--small.is-round {

+ 7 - 0
src/styles/icons.scss

@@ -72,6 +72,13 @@
   &-reexam {
     background-image: url(../assets/icon-reexam.png);
   }
+  &-seal {
+    background-image: url(../assets/icon-seal.png);
+  }
+  &-upload {
+    height: 12px;
+    background-image: url(../assets/icon-upload.png);
+  }
   &-analysis {
     background-image: url(../assets/icon-analysis.png);
   }