RealtimeMonitoring.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. <template>
  2. <div class="realtime-monitoring">
  3. <div class="realtime-top clear-float">
  4. <p>考场名称:{{ curExamBatch.name }}</p>
  5. <div
  6. class="el-select el-select--small"
  7. @click="$refs.ExamBatchDialog.open()"
  8. >
  9. <div class="el-input el-input--small el-input--suffix">
  10. <div class="el-input__inner">{{ curExamBatch.label }}</div>
  11. <span class="el-input__suffix">
  12. <span class="el-input__suffix-inner"
  13. ><i
  14. class="el-select__caret el-input__icon el-icon-arrow-up"
  15. ></i></span
  16. ></span>
  17. </div>
  18. </div>
  19. <text-clock></text-clock>
  20. </div>
  21. <div class="part-box-head">
  22. <div class="part-box-head-left">
  23. <h1>实时监控台</h1>
  24. </div>
  25. <div class="part-box-head-right">
  26. <div
  27. :class="[
  28. 'realtime-switch',
  29. { 'realtime-switch-warning': hasNewWarning },
  30. ]"
  31. >
  32. <div
  33. :class="[
  34. 'realtime-switch-item',
  35. { 'realtime-switch-item-act': pageType === '0' },
  36. ]"
  37. @click="pageTypeChange('0')"
  38. >
  39. <i class="el-icon-s-fold"></i><span>列表</span>
  40. </div>
  41. <div
  42. :class="[
  43. 'realtime-switch-item',
  44. { 'realtime-switch-item-act': pageType === '1' },
  45. ]"
  46. @click="pageTypeChange('1')"
  47. >
  48. <i class="el-icon-video-camera"></i><span>视频</span>
  49. </div>
  50. </div>
  51. </div>
  52. </div>
  53. <div class="part-filter">
  54. <div class="part-filter-info">
  55. <summary-line
  56. class="part-filter-info-main"
  57. data-type="trouble"
  58. :exam-id="filter.examId"
  59. ></summary-line>
  60. <div class="part-filter-info-sub">
  61. <el-button
  62. icon="el-icon-phone-outline"
  63. type="success"
  64. @click="toCommunication"
  65. >通话待办</el-button
  66. >
  67. </div>
  68. </div>
  69. <div class="part-filter-form">
  70. <el-form ref="FilterForm" label-position="left" inline>
  71. <el-form-item>
  72. <el-select
  73. v-model="filter.paperDownload"
  74. placeholder="试题下载"
  75. clearable
  76. >
  77. <el-option
  78. v-for="item in batchs"
  79. :key="item.id"
  80. :value="item.id"
  81. :label="item.name"
  82. ></el-option>
  83. </el-select>
  84. </el-form-item>
  85. <el-form-item>
  86. <el-select v-model="filter.status" placeholder="考试状态" clearable>
  87. <el-option
  88. v-for="item in exams"
  89. :key="item.id"
  90. :value="item.id"
  91. :label="item.name"
  92. ></el-option>
  93. </el-select>
  94. </el-form-item>
  95. <el-form-item>
  96. <el-select
  97. v-model="filter.monitorStatusSource"
  98. placeholder="通讯故障"
  99. clearable
  100. >
  101. <el-option
  102. v-for="item in exams"
  103. :key="item.id"
  104. :value="item.id"
  105. :label="item.name"
  106. ></el-option>
  107. </el-select>
  108. </el-form-item>
  109. <el-form-item label="预警量">
  110. <el-input-number
  111. style="width: 52px;"
  112. v-model.trim="filter.minWarningCount"
  113. placeholder="下限"
  114. :controls="false"
  115. ></el-input-number>
  116. <span class="line-split">-</span>
  117. <el-input-number
  118. style="width: 52px;"
  119. v-model.trim="filter.maxWarningCount"
  120. placeholder="上限"
  121. :controls="false"
  122. ></el-input-number>
  123. </el-form-item>
  124. <el-form-item>
  125. <el-input
  126. v-model.trim="filter.identity"
  127. placeholder="姓名/证件号"
  128. clearable
  129. ></el-input>
  130. </el-form-item>
  131. <el-form-item>
  132. <el-button type="primary" @click="toPage(1)">查询</el-button>
  133. </el-form-item>
  134. </el-form>
  135. <div class="part-filter-form-action">
  136. <el-dropdown
  137. @command="viewingAngleChange"
  138. style="margin-right: 10px;"
  139. v-if="pageType === '1'"
  140. >
  141. <el-button type="primary"
  142. >{{ curViewingAngle.name || "切换视角"
  143. }}<i class="el-icon-arrow-down el-icon--right"></i
  144. ></el-button>
  145. <el-dropdown-menu slot="dropdown">
  146. <el-dropdown-item
  147. v-for="item in viewingAngles"
  148. :key="item.code"
  149. :command="item"
  150. >切换{{ item.name }}</el-dropdown-item
  151. >
  152. </el-dropdown-menu>
  153. </el-dropdown>
  154. <el-button
  155. type="primary"
  156. icon="icon icon-handle"
  157. @click="finishInvigilation"
  158. >手动收卷</el-button
  159. >
  160. <el-button
  161. type="danger"
  162. icon="icon icon-over"
  163. @click="finishInvigilationExam"
  164. >结束监考</el-button
  165. >
  166. </div>
  167. </div>
  168. </div>
  169. <el-table
  170. ref="TableList"
  171. :data="dataList"
  172. @selection-change="handleSelectionChange"
  173. v-if="pageType === '0'"
  174. >
  175. <el-table-column type="selection" width="55" align="center">
  176. </el-table-column>
  177. <el-table-column prop="identity" label="证件号"></el-table-column>
  178. <el-table-column prop="name" label="姓名"></el-table-column>
  179. <el-table-column prop="courseName" label="科目名称"></el-table-column>
  180. <el-table-column prop="courseCode" label="科目代码"></el-table-column>
  181. <el-table-column prop="subjectCode" label="剩余时间"></el-table-column>
  182. <el-table-column prop="paperDownload" label="试题下载"></el-table-column>
  183. <el-table-column prop="status" label="考试状态"></el-table-column>
  184. <el-table-column prop="progress" label="进度"></el-table-column>
  185. <el-table-column
  186. prop="clientCommunicationStatus"
  187. label="通讯"
  188. ></el-table-column>
  189. <el-table-column prop="subjectCode" label="推流通讯"></el-table-column>
  190. <el-table-column prop="clientCurrentIp" label="IP"></el-table-column>
  191. <el-table-column prop="updateTime" label="更新时间"></el-table-column>
  192. <el-table-column prop="warningCount" label="预警数"></el-table-column>
  193. <el-table-column prop="breachStatus" label="违纪"></el-table-column>
  194. <el-table-column label="操作">
  195. <template slot-scope="scope">
  196. <el-button
  197. class="btn-table-icon"
  198. type="primary"
  199. icon="icon icon-view"
  200. @click="toDetail(scope.row)"
  201. >详情</el-button
  202. >
  203. </template>
  204. </el-table-column>
  205. </el-table>
  206. <div class="invigilation-student-list" v-else>
  207. <div
  208. class="invigilation-student-item"
  209. v-for="item in videoList"
  210. :key="item.examStudentId"
  211. >
  212. <invigilation-student :data="item"></invigilation-student>
  213. </div>
  214. </div>
  215. <div class="part-page">
  216. <el-pagination
  217. background
  218. layout="prev, pager, next,total,sizes,jumper"
  219. :current-page="current"
  220. :total="total"
  221. :page-size="size"
  222. @current-change="toPage"
  223. >
  224. </el-pagination>
  225. </div>
  226. <!-- 考试批次选择 -->
  227. <exam-batch-dialog
  228. @confirm="examChange"
  229. ref="ExamBatchDialog"
  230. ></exam-batch-dialog>
  231. <!-- 手动收卷 -->
  232. <handle-rollup-dialog
  233. :data-list="multipleSelection"
  234. @modified="rollupOver"
  235. ref="handleRollupDialog"
  236. ></handle-rollup-dialog>
  237. </div>
  238. </template>
  239. <script>
  240. import {
  241. invigilateList,
  242. invigilateVideoList,
  243. invigilateExamFinish,
  244. } from "@/api/invigilation";
  245. import ExamBatchDialog from "./ExamBatchDialog";
  246. import InvigilationStudent from "../common/InvigilationStudent";
  247. import SummaryLine from "../common/SummaryLine";
  248. import handleRollupDialog from "./handleRollupDialog";
  249. import TextClock from "../common/TextClock";
  250. export default {
  251. name: "realtime-monitoring",
  252. components: {
  253. ExamBatchDialog,
  254. InvigilationStudent,
  255. SummaryLine,
  256. handleRollupDialog,
  257. TextClock,
  258. },
  259. data() {
  260. return {
  261. filter: {
  262. examId: "",
  263. paperDownload: null,
  264. status: null,
  265. monitorStatusSource: null,
  266. identity: null,
  267. maxWarningCount: undefined,
  268. minWarningCount: undefined,
  269. },
  270. hasNewWarning: false,
  271. curExamBatch: {},
  272. curViewingAngle: {},
  273. current: 1,
  274. total: 0,
  275. size: 10,
  276. multipleSelection: [],
  277. batchId: "",
  278. batchs: [],
  279. exams: [],
  280. subjects: [],
  281. pageType: "0",
  282. dataList: [
  283. {
  284. breachStatus: 0,
  285. clientCommunicationStatus: "12",
  286. clientCurrentIp: "192.168.10.12",
  287. courseCode: "F001",
  288. courseName: "数学",
  289. examActivityId: 0,
  290. examId: 111,
  291. examRecordId: 222,
  292. examStudentId: 333,
  293. identity: "000000000000008",
  294. monitorStatusSource: "",
  295. name: "楚一一",
  296. paperDownload: 0,
  297. progress: 0,
  298. roomCode: "123",
  299. roomName: "第一教师",
  300. status: "1",
  301. statusCode: "1",
  302. updateTime: "2020-12-12",
  303. warningCount: 0,
  304. },
  305. ],
  306. videoList: [],
  307. viewingAngles: [
  308. {
  309. code: "1",
  310. name: "第一视角",
  311. },
  312. {
  313. code: "2",
  314. name: "第二视角",
  315. },
  316. {
  317. code: "3",
  318. name: "第三视角",
  319. },
  320. ],
  321. };
  322. },
  323. mounted() {},
  324. methods: {
  325. async getList() {
  326. const datas = {
  327. ...this.filter,
  328. pageNumber: this.current - 1,
  329. pageSize: this.size,
  330. };
  331. let res = null;
  332. if (this.pageType === "0") {
  333. res = await invigilateList(datas);
  334. this.dataList = res.data.data.records.map((item) => {
  335. item.label = `${item.identity} ${item.courseName}(${item.courseCode}) ${item.name}`;
  336. return item;
  337. });
  338. } else {
  339. res = await invigilateVideoList(datas);
  340. this.videoList = res.data.data.records;
  341. }
  342. this.total = res.data.data.records.total;
  343. },
  344. toPage(page) {
  345. this.current = page;
  346. this.getList();
  347. },
  348. examChange(examBatch) {
  349. if (!examBatch) return;
  350. this.filter.examId = examBatch.id;
  351. this.curExamBatch = examBatch;
  352. // this.toPage(1);
  353. },
  354. pageTypeChange(pageType) {
  355. this.pageType = pageType;
  356. this.toPage(1);
  357. this.multipleSelection = [];
  358. },
  359. handleSelectionChange(val) {
  360. console.log(val);
  361. this.multipleSelection = val;
  362. },
  363. viewingAngleChange(data) {
  364. this.curViewingAngle = data;
  365. },
  366. async finishInvigilation() {
  367. if (!this.multipleSelection.length) {
  368. this.$message.error("请先选择数据!");
  369. return;
  370. }
  371. this.$refs.handleRollupDialog.open();
  372. },
  373. rollupOver() {
  374. this.multipleSelection = [];
  375. this.getList();
  376. },
  377. async finishInvigilationExam() {
  378. const result = await this.$confirm("确定要结束监考吗?", {
  379. confirmButtonText: "确定",
  380. cancelButtonText: "取消",
  381. type: "confirm",
  382. }).catch(() => {});
  383. if (!result) return;
  384. await invigilateExamFinish();
  385. this.toPage(1);
  386. this.$message({
  387. type: "success",
  388. message: "操作成功!",
  389. });
  390. },
  391. toCommunication() {
  392. this.$router.push({ name: "VideoCommunication" });
  393. },
  394. toDetail(row) {
  395. console.log(row);
  396. this.$router.push({
  397. name: "WainingDetail",
  398. params: { recordId: row.examRecordId },
  399. });
  400. },
  401. },
  402. };
  403. </script>
  404. <style lang="scss" scoped>
  405. .realtime-top {
  406. position: relative;
  407. padding: 9px 20px 9px 73px;
  408. background: rgba(24, 134, 254, 1);
  409. border-radius: 6px;
  410. color: #fff;
  411. margin-bottom: 30px;
  412. &::before {
  413. content: "";
  414. display: block;
  415. position: absolute;
  416. width: 59px;
  417. height: 49px;
  418. left: 13px;
  419. top: 0;
  420. background-image: url(../../../assets/bg-stars.png);
  421. background-size: 100% 100%;
  422. }
  423. > * {
  424. float: left;
  425. line-height: 32px;
  426. margin: 0;
  427. }
  428. .el-select {
  429. min-width: 200px;
  430. }
  431. > p:first-child {
  432. margin-right: 40px;
  433. min-width: 150px;
  434. }
  435. > p:last-child {
  436. float: right;
  437. font-size: 12px;
  438. opacity: 0.8;
  439. }
  440. }
  441. .realtime-switch {
  442. font-size: 0;
  443. &-warning {
  444. .realtime-switch-item {
  445. &::before {
  446. content: "";
  447. display: block;
  448. position: absolute;
  449. width: 10px;
  450. height: 10px;
  451. top: -5px;
  452. right: -5px;
  453. border-radius: 50%;
  454. border: 2px solid #fff;
  455. background: #fe5863;
  456. z-index: 9;
  457. }
  458. }
  459. }
  460. &-item {
  461. display: inline-block;
  462. vertical-align: top;
  463. font-size: 12px;
  464. color: #8c94ac;
  465. background: #fff;
  466. line-height: 18px;
  467. padding: 5px 14px;
  468. position: relative;
  469. cursor: pointer;
  470. > i {
  471. margin-right: 5px;
  472. }
  473. &:first-child {
  474. border-radius: 6px 0px 0px 6px;
  475. }
  476. &:last-child {
  477. border-radius: 0px 6px 6px 0px;
  478. }
  479. &-act {
  480. color: #fff;
  481. background: #5fc9fa;
  482. }
  483. }
  484. }
  485. .invigilation-student-list {
  486. background: #ffffff;
  487. border-radius: 6px;
  488. padding: 10px 10px;
  489. font-size: 0;
  490. min-height: 200px;
  491. .invigilation-student-item {
  492. font-size: 14px;
  493. display: inline-block;
  494. vertical-align: top;
  495. padding: 10px;
  496. width: 25%;
  497. }
  498. }
  499. </style>