Bladeren bron

实时监控台视频控制

zhangjie 3 jaren geleden
bovenliggende
commit
cf8228e7c8

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

@@ -265,7 +265,11 @@
         v-for="item in dataList"
         :key="item.examRecordId"
       >
-        <invigilation-student :data="item"></invigilation-student>
+        <invigilation-student
+          ref="InvigilationStudent"
+          :data="item"
+          @muted="videoMuted"
+        ></invigilation-student>
       </div>
     </div>
 
@@ -607,6 +611,13 @@ export default {
         params: { recordId: row.examRecordId },
       });
     },
+    videoMuted(muted) {
+      if (muted) {
+        this.$refs.InvigilationStudent.forEach((refInst) => {
+          refInst.mutedPlayer(false);
+        });
+      }
+    },
   },
   beforeDestroy() {
     this.loopRunning = false;

+ 5 - 1
src/features/invigilation/common/FlvMedia.vue

@@ -9,6 +9,7 @@
 
 <script>
 import flvjs from "flv.js";
+// doc: https://github.com/bilibili/flv.js/blob/42343088f22619cf014a057b3878fe3d320016a6/docs/api.md
 
 export default {
   name: "flv-media",
@@ -34,7 +35,7 @@ export default {
         {
           type: "flv",
           isLive: true,
-          hasAudio: false,
+          hasAudio: true,
           url: this.liveUrl,
         },
         {
@@ -59,6 +60,9 @@ export default {
       this.flvPlayer.destroy();
       this.flvPlayer = null;
     },
+    mutedPlayer(muted) {
+      this.flvPlayer.muted = muted;
+    },
     reloadVideo() {
       this.destroyPlayer();
       this.initVideo();

+ 118 - 31
src/features/invigilation/common/InvigilationStudent.vue

@@ -1,6 +1,6 @@
 <template>
   <div :class="classes">
-    <div class="student-video">
+    <div class="student-video" title="点击放大播放" @click="toLargeView">
       <flv-media
         ref="ThirdViewVideo"
         :live-url="data.liveUrl"
@@ -8,30 +8,50 @@
       ></flv-media>
     </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>{{ data.progress || 0 }}%</span>
-      </p>
-      <div class="student-time">
-        <i class="icon icon-alarm-clock"></i>
-        <span>{{ data.remainTime }}</span>
+      <div class="student-info-header">
+        <h6 class="student-info-name">
+          <span>{{ data.name }}</span
+          ><i class="icon icon-net-break"></i>
+        </h6>
+        <div class="student-info-tips">
+          <div class="student-info-audio" @click="toMuted">
+            <i class="icon icon-audio"></i>
+          </div>
+          <div class="student-info-time">
+            <i class="icon icon-alarm-clock"></i>
+            <span>{{ data.remainTime }}</span>
+          </div>
+        </div>
+      </div>
+      <div class="student-info-body">
+        <div class="student-info-content">
+          <p>证件号:{{ data.identity }}</p>
+          <p>答题进度:{{ data.progress || 0 }}%</p>
+        </div>
+        <div class="student-info-detail">
+          <el-button type="primary" size="mini" round @click="toDetail"
+            >详情</el-button
+          >
+        </div>
       </div>
     </div>
+
+    <!-- InvigilationStudentMediaDialog -->
+    <InvigilationStudentMediaDialog
+      ref="InvigilationStudentMediaDialog"
+      :data="data"
+      destroy-on-close
+    />
   </div>
 </template>
 
 <script>
 import FlvMedia from "./FlvMedia";
+import InvigilationStudentMediaDialog from "./InvigilationStudentMediaDialog.vue";
 
 export default {
   name: "invigilation-student",
-  components: { FlvMedia },
+  components: { FlvMedia, InvigilationStudentMediaDialog },
   props: {
     data: {
       type: Object,
@@ -41,7 +61,9 @@ export default {
     },
   },
   data() {
-    return {};
+    return {
+      muted: false,
+    };
   },
   computed: {
     classes() {
@@ -54,7 +76,28 @@ export default {
       ];
     },
   },
-  methods: {},
+  methods: {
+    toDetail() {
+      this.$router.push({
+        name: "WarningDetail",
+        params: { recordId: this.data.examRecordId },
+      });
+    },
+    toLargeView() {
+      this.$refs.ThirdViewVideo.destroyPlayer();
+      this.$emit("muted", true);
+
+      this.$refs.InvigilationStudentMediaDialog.open();
+    },
+    toMuted() {
+      this.muted = !this.muted;
+      this.$emit("muted", this.muted);
+      this.mutedPlayer(this.muted);
+    },
+    mutedPlayer(muted) {
+      this.$refs.ThirdViewVideo.mutedPlayer(muted);
+    },
+  },
 };
 </script>
 
@@ -118,8 +161,9 @@ export default {
   height: 150px;
   border-radius: 6px;
   background: #626a82;
-  margin-bottom: 20px;
+  margin-bottom: 15px;
   position: relative;
+  cursor: pointer;
 
   &::before {
     content: "";
@@ -143,11 +187,38 @@ export default {
 .student-info {
   position: relative;
   padding: 0 10px 10px;
-  .student-time {
-    position: absolute;
+
+  &-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 5px;
+  }
+  &-audio {
+    display: inline-block;
+    vertical-align: middle;
+    width: 24px;
+    height: 24px;
+    text-align: center;
+    line-height: 24px;
+    background: #abb8c9;
+    border-radius: 50%;
+    margin-right: 5px;
+    cursor: pointer;
+    &:hover {
+      background: #1886fe;
+    }
+
+    .icon {
+      width: 16px;
+      height: 12px;
+      margin-top: -1px;
+    }
+  }
+  &-time {
+    display: inline-block;
+    vertical-align: middle;
     height: 24px;
-    top: 0;
-    right: 10px;
     padding: 4px 12px;
     border-radius: 12px;
     background: #abb8c9;
@@ -165,22 +236,38 @@ export default {
     }
   }
 
-  > h6 {
+  &-name {
     font-size: 18px;
-    line-height: 25px;
-    margin-bottom: 8;
+    margin-bottom: 0;
     > .icon {
       margin-top: -4px;
       margin-left: 8px;
       display: none;
     }
   }
-  > p {
-    font-size: 14px;
-    font-weight: 400;
-    color: #626a82;
-    line-height: 20px;
-    margin: 0;
+  &-body {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+  &-content {
+    flex-grow: 2;
+    overflow: hidden;
+    > p {
+      font-size: 14px;
+      font-weight: 400;
+      color: #626a82;
+      line-height: 20px;
+      margin: 0;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+  }
+  &-detail {
+    margin-left: 5px;
+    flex-grow: 0;
+    flex-shrink: 0;
   }
 }
 </style>

+ 49 - 0
src/features/invigilation/common/InvigilationStudentMediaDialog.vue

@@ -0,0 +1,49 @@
+<template>
+  <el-dialog
+    class="invigilation-student-media-dialog"
+    :visible.sync="dialogVisible"
+    :close-on-press-escape="false"
+    :close-on-click-modal="false"
+    append-to-body
+    fullscreen
+  >
+    <div class="media-close" @click="cancel"><i class="el-icon-close"></i></div>
+    <div class="media-video">
+      <flv-media
+        ref="ThirdViewVideo"
+        :live-url="data.liveUrl"
+        v-if="data.liveUrl"
+      ></flv-media>
+    </div>
+    <div class="media-info" slot="footer">
+      <p>{{ data.name }}</p>
+      <p>证件号:{{ data.identity }}</p>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  name: "invigilation-student-media-dialog",
+  props: {
+    data: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  data() {
+    return { dialogVisible: false };
+  },
+  methods: {
+    cancel() {
+      this.dialogVisible = false;
+      this.$emit("close");
+    },
+    open() {
+      this.dialogVisible = true;
+    },
+  },
+};
+</script>

+ 70 - 0
src/styles/base.scss

@@ -881,3 +881,73 @@ body {
     overflow: auto;
   }
 }
+
+// invigilation-student-media-dialog
+.invigilation-student-media-dialog {
+  .el-dialog {
+    background-color: rgba($color: #000000, $alpha: 0.8);
+    border-radius: 0;
+
+    .el-dialog__body {
+      height: 100%;
+      padding-bottom: 60px;
+    }
+  }
+  .el-dialog__header {
+    display: none;
+  }
+  .el-dialog__footer {
+    position: absolute;
+    width: 100%;
+    bottom: 0;
+    left: 0;
+    z-index: 9;
+    padding: 10px;
+    border-top: 1px solid #333;
+    text-align: left;
+    color: #888;
+  }
+  .media-close {
+    position: absolute;
+    width: 40px;
+    height: 40px;
+    line-height: 40px;
+    text-align: center;
+    top: 10px;
+    right: 10px;
+    font-size: 32px;
+    z-index: 99;
+    cursor: pointer;
+    &:hover {
+      color: #fff;
+    }
+  }
+  .media-video {
+    height: 100%;
+
+    &::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%;
+    }
+  }
+  .media-info p {
+    display: inline-block;
+    vertical-align: middle;
+    margin-right: 20px;
+    margin-bottom: 0;
+
+    &:first-child {
+      font-size: 16px;
+      color: #1886fe;
+    }
+  }
+}