Pārlūkot izejas kodu

图片预览调整

zhangjie 3 gadi atpakaļ
vecāks
revīzija
6db1abe451

+ 306 - 0
src/components/imagePreview/SimpleImagePreview.vue

@@ -0,0 +1,306 @@
+<template>
+  <el-dialog
+    :class="prefixCls"
+    title="图片预览"
+    :visible.sync="modalIsShow"
+    :close-on-press-escape="false"
+    :close-on-click-modal="false"
+    append-to-body
+    fullscreen
+    destroy-on-close
+    @opened="dialogOpen"
+    @close="dialogClose"
+  >
+    <div slot="title"></div>
+    <div :class="[`${prefixCls}-close`]" @click="cancel">
+      <i class="el-icon-circle-close"></i>
+    </div>
+
+    <div :class="[`${prefixCls}-body`]" ref="ReviewBody">
+      <!-- <div
+        :class="[`${prefixCls}-guide`, `${prefixCls}-guide-prev`]"
+        @click.stop="showPrev"
+      >
+        <i class="el-icon-arrow-left"></i>
+      </div>
+      <div
+        :class="[`${prefixCls}-guide`, `${prefixCls}-guide-next`]"
+        @click.stop="showNext"
+      >
+        <i class="el-icon-arrow-right"></i>
+      </div> -->
+      <div
+        :class="[
+          `${prefixCls}-imgs`,
+          { [`${prefixCls}-imgs-nosition`]: nosition },
+        ]"
+        :style="styles"
+        v-move-ele.prevent.stop="{ mouseMove }"
+        v-if="modalIsShow"
+      >
+        <img
+          :key="curImage.imgSrc"
+          :src="curImage.imgSrc"
+          :alt="curImage.name"
+          ref="PreviewImgDetail"
+          @load="resizeImage"
+        />
+      </div>
+      <div :class="[`${prefixCls}-none`]" v-if="!curImage.imgSrc">
+        <i class="el-icon-picture"></i>
+        <p>暂无数据</p>
+      </div>
+
+      <div :class="[`${prefixCls}-loading`]" v-show="loading">
+        <i class="el-icon-loading"></i>
+      </div>
+    </div>
+
+    <div :class="[`${prefixCls}-footer`]">
+      <ul>
+        <li title="合适大小" @click="toOrigin">
+          <i class="el-icon-rank"></i>
+        </li>
+        <li
+          title="放大"
+          @click="toMagnify"
+          :class="{
+            'li-disabled': transform.scale === maxScale,
+          }"
+        >
+          <i class="el-icon-zoom-in"></i>
+        </li>
+        <li
+          title="缩小"
+          @click="toShrink"
+          :class="{
+            'li-disabled': transform.scale === minScale,
+          }"
+        >
+          <i class="el-icon-zoom-out"></i>
+        </li>
+        <li title="旋转" @click.stop="toRotate">
+          <i class="el-icon-refresh"></i>
+        </li>
+      </ul>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import MoveEle from "./move-ele";
+const prefixCls = "cc-image-preview";
+
+export default {
+  name: "simple-image-preview",
+  props: {
+    curImage: {
+      type: Object,
+      default() {
+        return {};
+      },
+    },
+  },
+  directives: { MoveEle },
+  data() {
+    return {
+      prefixCls,
+      modalIsShow: false,
+      styles: { width: "", height: "", top: "", left: "", transform: "" },
+      initWidth: 500,
+      minScale: 0.2,
+      maxScale: 5,
+      transform: {
+        scale: 1,
+        rotate: 0,
+      },
+      loading: false,
+      loadingSetT: null,
+      nosition: false,
+    };
+  },
+  // watch: {
+  //   "curImage.imgSrc": {
+  //     handler(val) {
+  //       if (val) {
+  //         this.loadingSetT = setTimeout(() => {
+  //           this.loading = true;
+  //         }, 300);
+  //         this.styles = {
+  //           width: "",
+  //           height: "",
+  //           top: "",
+  //           left: "",
+  //           transform: ""
+  //         };
+  //       }
+  //     }
+  //   }
+  // },
+  methods: {
+    dialogClose() {
+      this.$refs.ReviewBody.removeEventListener("wheel", this.mouseWheel);
+    },
+    dialogOpen() {
+      this.$refs.ReviewBody.addEventListener("wheel", this.mouseWheel);
+    },
+    resizeImage() {
+      if (this.loadingSetT) clearTimeout(this.loadingSetT);
+
+      const imgDom = this.$refs.PreviewImgDetail;
+      const { naturalWidth, naturalHeight } = imgDom;
+      const imageSize = this.getImageSizePos({
+        win: {
+          width: this.$refs.ReviewBody.clientWidth,
+          height: this.$refs.ReviewBody.clientHeight,
+        },
+        img: {
+          width: naturalWidth,
+          height: naturalHeight,
+        },
+        rotate: 0,
+      });
+
+      this.styles = Object.assign(this.styles, {
+        width: imageSize.width + "px",
+        height: imageSize.height + "px",
+        top: imageSize.top + "px",
+        left: imageSize.left + "px",
+        marginLeft: "auto",
+        transform: "none",
+      });
+      this.transform = {
+        scale: 1,
+        rotate: 0,
+      };
+      this.loading = false;
+      setTimeout(() => {
+        this.nosition = false;
+      }, 100);
+    },
+    getImageSizePos({ win, img, rotate }) {
+      const imageSize = {
+        width: 0,
+        height: 0,
+        top: 0,
+        left: 0,
+      };
+      const isHorizontal = !!(rotate % 180);
+
+      const rateWin = isHorizontal
+        ? win.height / win.width
+        : win.width / win.height;
+      const hwin = isHorizontal
+        ? {
+            width: win.height,
+            height: win.width,
+          }
+        : win;
+
+      const rateImg = img.width / img.height;
+
+      if (rateImg <= rateWin) {
+        imageSize.height = Math.min(hwin.height, img.height);
+        imageSize.width = Math.floor(
+          (imageSize.height * img.width) / img.height
+        );
+      } else {
+        imageSize.width = Math.min(hwin.width, img.width);
+        imageSize.height = Math.floor(
+          (imageSize.width * img.height) / img.width
+        );
+      }
+      imageSize.left = (win.width - imageSize.width) / 2;
+      imageSize.top = (win.height - imageSize.height) / 2;
+      return imageSize;
+    },
+    cancel() {
+      this.modalIsShow = false;
+      this.$emit("on-close");
+    },
+    open() {
+      this.modalIsShow = true;
+    },
+    showPrev() {
+      this.$emit("on-prev");
+      // this.initData();
+    },
+    showNext() {
+      this.$emit("on-next");
+      // this.initData();
+    },
+    // dome-move
+    registWheelHandle() {
+      this.$refs.ReviewBody.addEventListener("wheel", this.mouseWheel);
+    },
+    mouseMove({ left, top }) {
+      this.styles.left = left + "px";
+      this.styles.top = top + "px";
+    },
+    mouseWheel(e) {
+      e.preventDefault();
+
+      if (e.wheelDeltaY < 0) {
+        this.toMagnify();
+      } else {
+        this.toShrink();
+      }
+    },
+    setStyleTransform() {
+      const { scale, rotate } = this.transform;
+      this.styles.transform = `scale(${scale}, ${scale}) rotate(${rotate}deg)`;
+    },
+    toOrigin() {
+      this.transform.scale = 1;
+      this.setStyleTransform();
+      this.resizeImage();
+    },
+    toMagnify() {
+      const scale = (this.transform.scale * 1.2).toFixed(2);
+      this.transform.scale = scale >= this.maxScale ? this.maxScale : scale;
+      this.setStyleTransform();
+    },
+    toShrink() {
+      const scale = (this.transform.scale * 0.75).toFixed(2);
+      this.transform.scale = scale <= this.minScale ? this.minScale : scale;
+      this.setStyleTransform();
+    },
+    toRotate() {
+      this.transform.rotate = this.transform.rotate + 90;
+      this.setStyleTransform();
+      // 调整图片尺寸
+      const { naturalWidth, naturalHeight } = this.$refs.PreviewImgDetail;
+      const imageSize = this.getImageSizePos({
+        win: {
+          width: this.$refs.ReviewBody.clientWidth,
+          height: this.$refs.ReviewBody.clientHeight,
+        },
+        img: {
+          width: naturalWidth,
+          height: naturalHeight,
+        },
+        rotate: this.transform.rotate,
+      });
+
+      this.styles = Object.assign(this.styles, {
+        width: imageSize.width + "px",
+        height: imageSize.height + "px",
+        top: imageSize.top + "px",
+        left: imageSize.left + "px",
+      });
+      // 360度无缝切换到0度
+      if (this.transform.rotate >= 360) {
+        setTimeout(() => {
+          this.nosition = true;
+          this.transform.rotate = 0;
+          this.setStyleTransform();
+          setTimeout(() => {
+            this.nosition = false;
+          }, 100);
+        }, 200);
+        // 200ms当次旋转动画持续时间
+      }
+    },
+  },
+};
+</script>

+ 56 - 0
src/components/imagePreview/move-ele.js

@@ -0,0 +1,56 @@
+export default {
+  inserted(el, { value, modifiers }) {
+    let [_x, _y] = [0, 0];
+    // 当前拖动事务开始前元素的left,top
+    let [oleft, otop] = [0, 0];
+    // 元素移动后的left,top
+    let [left, top] = [0, 0];
+    let isDrag = false;
+
+    let moveHandle = function (e) {
+      isDrag = true;
+      if (modifiers.prevent) {
+        e.preventDefault();
+      }
+      left = oleft + e.pageX - _x;
+      top = otop + e.pageY - _y;
+
+      if (value && value.mouseMove) {
+        value.mouseMove({ left, top });
+      } else {
+        el.style.left = left + "px";
+        el.style.top = top + "px";
+      }
+    };
+
+    let upHandle = function (e) {
+      if (modifiers.prevent) {
+        e.preventDefault();
+      }
+      oleft = left;
+      otop = top;
+
+      if (value && value.mouseUp) value.mouseUp({ oleft, otop });
+      if (value && value.click && !isDrag) value.click();
+
+      document.removeEventListener("mousemove", moveHandle);
+      document.removeEventListener("mouseup", upHandle);
+
+      isDrag = false;
+    };
+
+    el.addEventListener("mousedown", function (e) {
+      if (modifiers.prevent) {
+        e.preventDefault();
+      }
+      _x = e.pageX;
+      _y = e.pageY;
+      oleft = el.offsetLeft;
+      otop = el.offsetTop;
+      if (value && value.mouseDown) value.mouseDown({ oleft, otop });
+
+      document.addEventListener("mousemove", moveHandle);
+      document.addEventListener("mouseup", upHandle);
+    });
+  },
+};

+ 9 - 0
src/features/invigilation/ExamInvigilation/ExamInvigilation.vue

@@ -237,7 +237,9 @@
       <invigilation-student
       <invigilation-student
         v-for="item in students"
         v-for="item in students"
         :key="item.examStudentId"
         :key="item.examStudentId"
+        ref="InvigilationStudent"
         :data="item"
         :data="item"
+        @muted="videoMuted"
       ></invigilation-student>
       ></invigilation-student>
     </div>
     </div>
   </div>
   </div>
@@ -412,6 +414,13 @@ export default {
       this.typeWarningData.type = colorType;
       this.typeWarningData.type = colorType;
       this.warningTrendData.type = colorType;
       this.warningTrendData.type = colorType;
     },
     },
+    videoMuted(muted) {
+      if (!muted) {
+        this.$refs.InvigilationStudent.forEach((refInst) => {
+          refInst.mutedPlayer(true);
+        });
+      }
+    },
   },
   },
   beforeDestroy() {
   beforeDestroy() {
     this.loopRunning = false;
     this.loopRunning = false;

+ 12 - 1
src/features/invigilation/OnlinePatrol/PatrolExamDetail.vue

@@ -231,7 +231,11 @@
         v-for="item in dataList"
         v-for="item in dataList"
         :key="item.examRecordId"
         :key="item.examRecordId"
       >
       >
-        <invigilation-student :data="item"></invigilation-student>
+        <invigilation-student
+          ref="InvigilationStudent"
+          :data="item"
+          @muted="videoMuted"
+        ></invigilation-student>
       </div>
       </div>
     </div>
     </div>
 
 
@@ -449,6 +453,13 @@ export default {
         params: { recordId: row.examRecordId },
         params: { recordId: row.examRecordId },
       });
       });
     },
     },
+    videoMuted(muted) {
+      if (!muted) {
+        this.$refs.InvigilationStudent.forEach((refInst) => {
+          refInst.mutedPlayer(true);
+        });
+      }
+    },
     goBack() {
     goBack() {
       window.history.go(-1);
       window.history.go(-1);
     },
     },

+ 48 - 7
src/features/invigilation/OnlinePatrol/PatrolWarningDetail.vue

@@ -64,6 +64,16 @@
               <div class="student-video-none" v-else>
               <div class="student-video-none" v-else>
                 <i class="el-icon-video-camera-solid"></i>
                 <i class="el-icon-video-camera-solid"></i>
               </div>
               </div>
+              <div
+                v-if="firstViewVideoReady"
+                :class="[
+                  'student-video-muted',
+                  { 'is-active': !firstViewVideo.muted },
+                ]"
+                @click="videoMute('first')"
+              >
+                <i class="icon icon-audio"></i>
+              </div>
             </div>
             </div>
             <div class="student-video-item">
             <div class="student-video-item">
               <flv-media
               <flv-media
@@ -74,6 +84,16 @@
               <div class="student-video-none" v-else>
               <div class="student-video-none" v-else>
                 <i class="el-icon-video-camera-solid"></i>
                 <i class="el-icon-video-camera-solid"></i>
               </div>
               </div>
+              <div
+                v-if="secondViewVideoReady"
+                :class="[
+                  'student-video-muted',
+                  { 'is-active': !secondViewVideo.muted },
+                ]"
+                @click="videoMute('second')"
+              >
+                <i class="icon icon-audio"></i>
+              </div>
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>
@@ -163,22 +183,24 @@
           <div class="warning-history-media">
           <div class="warning-history-media">
             <ul class="media-list" v-if="log.photos">
             <ul class="media-list" v-if="log.photos">
               <li v-for="(photo, pindex) in log.photos" :key="pindex">
               <li v-for="(photo, pindex) in log.photos" :key="pindex">
-                <!-- <img :src="photo" /> -->
-                <el-image
-                  :src="photo"
-                  fit="contain"
-                  :preview-src-list="[photo]"
-                ></el-image>
+                <img :src="photo" @click="toViewImg(photo)" />
               </li>
               </li>
             </ul>
             </ul>
           </div>
           </div>
         </div>
         </div>
       </div>
       </div>
     </div>
     </div>
+
+    <!-- image-preview -->
+    <simple-image-preview
+      :cur-image="curImage"
+      ref="SimpleImagePreview"
+    ></simple-image-preview>
   </div>
   </div>
 </template>
 </template>
 
 
 <script>
 <script>
+import SimpleImagePreview from "@/components/imagePreview/SimpleImagePreview";
 import FlvMedia from "../common/FlvMedia";
 import FlvMedia from "../common/FlvMedia";
 import { invigilateDetail, warningStudentDetail } from "@/api/invigilation";
 import { invigilateDetail, warningStudentDetail } from "@/api/invigilation";
 import { formatDate, timeNumberToText, objTypeOf } from "@/utils/utils";
 import { formatDate, timeNumberToText, objTypeOf } from "@/utils/utils";
@@ -186,7 +208,7 @@ import { mapState } from "vuex";
 
 
 export default {
 export default {
   name: "patrol-warning-detail",
   name: "patrol-warning-detail",
-  components: { FlvMedia },
+  components: { FlvMedia, SimpleImagePreview },
   data() {
   data() {
     return {
     return {
       recordId: this.$route.params.recordId,
       recordId: this.$route.params.recordId,
@@ -196,13 +218,16 @@ export default {
       exceptionSummary: [],
       exceptionSummary: [],
       firstViewVideo: {
       firstViewVideo: {
         liveUrl: "",
         liveUrl: "",
+        muted: true,
       },
       },
       secondViewVideo: {
       secondViewVideo: {
         liveUrl: "",
         liveUrl: "",
+        muted: true,
       },
       },
       firstViewVideoReady: false,
       firstViewVideoReady: false,
       secondViewVideoReady: false,
       secondViewVideoReady: false,
       holding: false,
       holding: false,
+      curImage: { imgSrc: "" },
     };
     };
   },
   },
   computed: {
   computed: {
@@ -238,6 +263,7 @@ export default {
           ? `${domain}/live/${item.liveUrl.toLowerCase()}.flv`
           ? `${domain}/live/${item.liveUrl.toLowerCase()}.flv`
           : "";
           : "";
         item.name = item.source;
         item.name = item.source;
+        item.muted = true;
         return item;
         return item;
       });
       });
       const orderSources = {
       const orderSources = {
@@ -381,6 +407,10 @@ export default {
         },
         },
       });
       });
     },
     },
+    toViewImg(photo) {
+      this.curImage = { imgSrc: photo };
+      this.$refs.SimpleImagePreview.open();
+    },
     // video relative
     // video relative
     initSubscribeVideo() {
     initSubscribeVideo() {
       if (this.firstViewVideo.liveUrl) this.firstViewVideoReady = true;
       if (this.firstViewVideo.liveUrl) this.firstViewVideoReady = true;
@@ -390,6 +420,17 @@ export default {
       this.firstViewVideoReady = false;
       this.firstViewVideoReady = false;
       this.secondViewVideoReady = false;
       this.secondViewVideoReady = false;
     },
     },
+    videoMute(type) {
+      if (type === "first") {
+        this.firstViewVideo.muted = !this.firstViewVideo.muted;
+        this.secondViewVideo.muted = true;
+        this.$refs.FirstViewVideo.mutedPlayer(this.firstViewVideo.muted);
+      } else {
+        this.secondViewVideo.muted = !this.secondViewVideo.muted;
+        this.firstViewVideo.muted = true;
+        this.$refs.SecondViewVideo.mutedPlayer(this.secondViewVideo.muted);
+      }
+    },
     goBack() {
     goBack() {
       window.history.go(-1);
       window.history.go(-1);
     },
     },

+ 2 - 2
src/features/invigilation/RealtimeMonitoring/RealtimeMonitoring.vue

@@ -612,9 +612,9 @@ export default {
       });
       });
     },
     },
     videoMuted(muted) {
     videoMuted(muted) {
-      if (muted) {
+      if (!muted) {
         this.$refs.InvigilationStudent.forEach((refInst) => {
         this.$refs.InvigilationStudent.forEach((refInst) => {
-          refInst.mutedPlayer(false);
+          refInst.mutedPlayer(true);
         });
         });
       }
       }
     },
     },

+ 47 - 6
src/features/invigilation/RealtimeMonitoring/WarningDetail.vue

@@ -78,6 +78,16 @@
               <div class="student-video-none" v-else>
               <div class="student-video-none" v-else>
                 <i class="el-icon-video-camera-solid"></i>
                 <i class="el-icon-video-camera-solid"></i>
               </div>
               </div>
+              <div
+                v-if="firstViewVideoReady"
+                :class="[
+                  'student-video-muted',
+                  { 'is-active': !firstViewVideo.muted },
+                ]"
+                @click="videoMute('first')"
+              >
+                <i class="icon icon-audio"></i>
+              </div>
             </div>
             </div>
             <div class="student-video-item">
             <div class="student-video-item">
               <flv-media
               <flv-media
@@ -88,6 +98,16 @@
               <div class="student-video-none" v-else>
               <div class="student-video-none" v-else>
                 <i class="el-icon-video-camera-solid"></i>
                 <i class="el-icon-video-camera-solid"></i>
               </div>
               </div>
+              <div
+                v-if="secondViewVideoReady"
+                :class="[
+                  'student-video-muted',
+                  { 'is-active': !secondViewVideo.muted },
+                ]"
+                @click="videoMute('second')"
+              >
+                <i class="icon icon-audio"></i>
+              </div>
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>
@@ -227,12 +247,7 @@
           <div class="warning-history-media">
           <div class="warning-history-media">
             <ul class="media-list" v-if="log.photos">
             <ul class="media-list" v-if="log.photos">
               <li v-for="(photo, pindex) in log.photos" :key="pindex">
               <li v-for="(photo, pindex) in log.photos" :key="pindex">
-                <!-- <img :src="photo" /> -->
-                <el-image
-                  :src="photo"
-                  fit="contain"
-                  :preview-src-list="[photo]"
-                ></el-image>
+                <img :src="photo" @click="toViewImg(photo)" />
               </li>
               </li>
             </ul>
             </ul>
           </div>
           </div>
@@ -256,6 +271,11 @@
       :record-id="recordId"
       :record-id="recordId"
       ref="AudioRecordDialog"
       ref="AudioRecordDialog"
     ></audio-record-dialog>
     ></audio-record-dialog>
+    <!-- image-preview -->
+    <simple-image-preview
+      :cur-image="curImage"
+      ref="SimpleImagePreview"
+    ></simple-image-preview>
 
 
     <!-- 通话弹出层 -->
     <!-- 通话弹出层 -->
     <el-dialog
     <el-dialog
@@ -315,6 +335,7 @@ import FlvMedia from "../common/FlvMedia";
 import StudentBreachDialog from "./StudentBreachDialog";
 import StudentBreachDialog from "./StudentBreachDialog";
 import WarningTextMessageDialog from "./WarningTextMessageDialog";
 import WarningTextMessageDialog from "./WarningTextMessageDialog";
 import AudioRecordDialog from "./audioRecord/AudioRecordDialog";
 import AudioRecordDialog from "./audioRecord/AudioRecordDialog";
+import SimpleImagePreview from "@/components/imagePreview/SimpleImagePreview";
 import SecondTimer from "../common/SecondTimer";
 import SecondTimer from "../common/SecondTimer";
 import { formatDate, timeNumberToText, objTypeOf } from "@/utils/utils";
 import { formatDate, timeNumberToText, objTypeOf } from "@/utils/utils";
 import { mapState } from "vuex";
 import { mapState } from "vuex";
@@ -332,6 +353,7 @@ export default {
     StudentBreachDialog,
     StudentBreachDialog,
     WarningTextMessageDialog,
     WarningTextMessageDialog,
     AudioRecordDialog,
     AudioRecordDialog,
+    SimpleImagePreview,
     SecondTimer,
     SecondTimer,
   },
   },
   data() {
   data() {
@@ -343,9 +365,11 @@ export default {
       exceptionSummary: [],
       exceptionSummary: [],
       firstViewVideo: {
       firstViewVideo: {
         liveUrl: "",
         liveUrl: "",
+        muted: true,
       },
       },
       secondViewVideo: {
       secondViewVideo: {
         liveUrl: "",
         liveUrl: "",
+        muted: true,
       },
       },
       firstViewVideoReady: false,
       firstViewVideoReady: false,
       secondViewVideoReady: false,
       secondViewVideoReady: false,
@@ -361,6 +385,7 @@ export default {
       loopRunning: false,
       loopRunning: false,
       loopSetTs: [],
       loopSetTs: [],
       isHandup: false,
       isHandup: false,
+      curImage: { imgSrc: "" },
     };
     };
   },
   },
   computed: {
   computed: {
@@ -429,6 +454,7 @@ export default {
           ? `${domain}/live/${item.liveUrl.toLowerCase()}.flv`
           ? `${domain}/live/${item.liveUrl.toLowerCase()}.flv`
           : "";
           : "";
         item.name = item.source;
         item.name = item.source;
+        item.muted = true;
         return item;
         return item;
       });
       });
       const orderSources = {
       const orderSources = {
@@ -812,6 +838,21 @@ export default {
       this.isWaiting = true;
       this.isWaiting = true;
       this.initSubscribeVideo();
       this.initSubscribeVideo();
     },
     },
+    videoMute(type) {
+      if (type === "first") {
+        this.firstViewVideo.muted = !this.firstViewVideo.muted;
+        this.secondViewVideo.muted = true;
+        this.$refs.FirstViewVideo.mutedPlayer(this.firstViewVideo.muted);
+      } else {
+        this.secondViewVideo.muted = !this.secondViewVideo.muted;
+        this.firstViewVideo.muted = true;
+        this.$refs.SecondViewVideo.mutedPlayer(this.secondViewVideo.muted);
+      }
+    },
+    toViewImg(photo) {
+      this.curImage = { imgSrc: photo };
+      this.$refs.SimpleImagePreview.open();
+    },
     goBack() {
     goBack() {
       window.history.go(-1);
       window.history.go(-1);
     },
     },

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

@@ -2,8 +2,8 @@
   <video
   <video
     class="flv-media"
     class="flv-media"
     ref="VideoMedia"
     ref="VideoMedia"
-    @ended="destroyPlayer"
     muted
     muted
+    @ended="destroyPlayer"
   ></video>
   ></video>
 </template>
 </template>
 
 
@@ -61,6 +61,7 @@ export default {
       this.flvPlayer = null;
       this.flvPlayer = null;
     },
     },
     mutedPlayer(muted) {
     mutedPlayer(muted) {
+      if (!this.flvPlayer) return;
       this.flvPlayer.muted = muted;
       this.flvPlayer.muted = muted;
     },
     },
     reloadVideo() {
     reloadVideo() {

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

@@ -14,7 +14,10 @@
           ><i class="icon icon-net-break"></i>
           ><i class="icon icon-net-break"></i>
         </h6>
         </h6>
         <div class="student-info-tips">
         <div class="student-info-tips">
-          <div class="student-info-audio" @click="toMuted">
+          <div
+            :class="['student-info-audio', { 'is-active': !this.muted }]"
+            @click="toMuted"
+          >
             <i class="icon icon-audio"></i>
             <i class="icon icon-audio"></i>
           </div>
           </div>
           <div class="student-info-time">
           <div class="student-info-time">
@@ -41,6 +44,7 @@
       ref="InvigilationStudentMediaDialog"
       ref="InvigilationStudentMediaDialog"
       :data="data"
       :data="data"
       destroy-on-close
       destroy-on-close
+      @close="dialogClose"
     />
     />
   </div>
   </div>
 </template>
 </template>
@@ -62,7 +66,7 @@ export default {
   },
   },
   data() {
   data() {
     return {
     return {
-      muted: false,
+      muted: true,
     };
     };
   },
   },
   computed: {
   computed: {
@@ -84,12 +88,13 @@ export default {
       });
       });
     },
     },
     toLargeView() {
     toLargeView() {
+      this.muted = true;
+      this.$emit("muted", false);
       this.$refs.ThirdViewVideo.destroyPlayer();
       this.$refs.ThirdViewVideo.destroyPlayer();
-      this.$emit("muted", true);
-
       this.$refs.InvigilationStudentMediaDialog.open();
       this.$refs.InvigilationStudentMediaDialog.open();
     },
     },
     toMuted() {
     toMuted() {
+      console.log(this.muted);
       this.muted = !this.muted;
       this.muted = !this.muted;
       this.$emit("muted", this.muted);
       this.$emit("muted", this.muted);
       this.mutedPlayer(this.muted);
       this.mutedPlayer(this.muted);
@@ -97,6 +102,9 @@ export default {
     mutedPlayer(muted) {
     mutedPlayer(muted) {
       this.$refs.ThirdViewVideo.mutedPlayer(muted);
       this.$refs.ThirdViewVideo.mutedPlayer(muted);
     },
     },
+    dialogClose() {
+      this.$refs.ThirdViewVideo.reloadVideo();
+    },
   },
   },
 };
 };
 </script>
 </script>
@@ -165,19 +173,19 @@ export default {
   position: relative;
   position: relative;
   cursor: pointer;
   cursor: pointer;
 
 
-  &::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%;
-  }
+  // &::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 {
   > video {
     width: 100%;
     width: 100%;
@@ -209,10 +217,14 @@ export default {
       background: #1886fe;
       background: #1886fe;
     }
     }
 
 
+    &.is-active {
+      background: #1886fe;
+    }
+
     .icon {
     .icon {
-      width: 16px;
+      width: 14px;
       height: 12px;
       height: 12px;
-      margin-top: -1px;
+      margin-top: -2px;
     }
     }
   }
   }
   &-time {
   &-time {

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

@@ -6,6 +6,8 @@
     :close-on-click-modal="false"
     :close-on-click-modal="false"
     append-to-body
     append-to-body
     fullscreen
     fullscreen
+    destroy-on-close
+    @opened="dialogOpened"
   >
   >
     <div class="media-close" @click="cancel"><i class="el-icon-close"></i></div>
     <div class="media-close" @click="cancel"><i class="el-icon-close"></i></div>
     <div class="media-video">
     <div class="media-video">
@@ -23,8 +25,11 @@
 </template>
 </template>
 
 
 <script>
 <script>
+import FlvMedia from "./FlvMedia";
+
 export default {
 export default {
   name: "invigilation-student-media-dialog",
   name: "invigilation-student-media-dialog",
+  components: { FlvMedia },
   props: {
   props: {
     data: {
     data: {
       type: Object,
       type: Object,
@@ -44,6 +49,11 @@ export default {
     open() {
     open() {
       this.dialogVisible = true;
       this.dialogVisible = true;
     },
     },
+    dialogOpened() {
+      this.$nextTick(() => {
+        this.$refs.ThirdViewVideo.mutedPlayer(false);
+      });
+    },
   },
   },
 };
 };
 </script>
 </script>

+ 210 - 20
src/styles/base.scss

@@ -436,6 +436,33 @@ body {
       background: #606060;
       background: #606060;
     }
     }
 
 
+    &-muted {
+      position: absolute;
+      width: 24px;
+      height: 24px;
+      bottom: 0;
+      right: 0;
+      z-index: 99;
+      text-align: center;
+      line-height: 24px;
+      background: #abb8c9;
+      border-radius: 50%;
+      margin-right: 5px;
+      cursor: pointer;
+      &:hover {
+        background: #1886fe;
+      }
+
+      &.is-active {
+        background: #1886fe;
+      }
+      .icon {
+        width: 14px;
+        height: 12px;
+        margin-top: -2px;
+      }
+    }
+
     &-main {
     &-main {
       position: absolute;
       position: absolute;
       top: 0;
       top: 0;
@@ -660,16 +687,12 @@ body {
       background-color: #e8edf3;
       background-color: #e8edf3;
       overflow: hidden;
       overflow: hidden;
 
 
-      .el-image {
-        display: block;
+      > img {
+        width: 100%;
         height: 100%;
         height: 100%;
+        object-fit: contain;
+        cursor: pointer;
       }
       }
-
-      // > img {
-      //   width: 100%;
-      //   height: 100%;
-      //   object-fit: contain;
-      // }
     }
     }
   }
   }
 }
 }
@@ -925,18 +948,9 @@ body {
   .media-video {
   .media-video {
     height: 100%;
     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%;
+    video {
+      width: 100%;
+      height: 100%;
     }
     }
   }
   }
   .media-info p {
   .media-info p {
@@ -970,3 +984,179 @@ body {
     height: auto;
     height: auto;
   }
   }
 }
 }
+
+.cc-image-preview {
+  &-header {
+    position: absolute;
+    width: 100%;
+    padding: 15px;
+    top: 0;
+    left: 0;
+    line-height: 20px;
+    text-align: center;
+    font-size: 16px;
+    z-index: 99;
+    color: #f0f0f0;
+    h3 {
+      position: absolute;
+      left: 15px;
+      z-index: auto;
+    }
+  }
+  &-close {
+    position: absolute;
+    width: 30px;
+    height: 30px;
+    top: 5px;
+    right: 15px;
+    z-index: 100;
+    line-height: 30px;
+    text-align: center;
+    font-size: 40px;
+    color: #fff;
+    text-shadow: 0 0 2px #333;
+    cursor: pointer;
+
+    &:hover {
+      color: #f56c6c;
+    }
+  }
+  &-body {
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    bottom: 0;
+    z-index: auto;
+    overflow: hidden;
+  }
+  &-imgs {
+    position: absolute;
+    top: 0;
+    left: 50%;
+    width: 600px;
+    margin-left: -300px;
+    // box-shadow: 0px 24px 36px 0px rgba(0, 0, 0, 0.3);
+    transition: width, height, transform 0.2s linear;
+    z-index: 8;
+    &-nosition {
+      transition: none;
+    }
+    &-move {
+      cursor: move;
+    }
+    > img {
+      display: block;
+      width: 100%;
+    }
+  }
+  &-guide {
+    position: absolute;
+    width: 80px;
+    height: 80px;
+    line-height: 80px;
+    top: 50%;
+    margin-top: -80px;
+    text-align: center;
+    color: #d0d0d0;
+    z-index: 9;
+    font-size: 60px;
+    text-shadow: 0 0 2px #333;
+    cursor: pointer;
+
+    &:hover {
+      color: #eee;
+    }
+    > i {
+      margin-top: -5px;
+    }
+    &-prev {
+      left: 0;
+    }
+    &-next {
+      right: 0;
+    }
+  }
+  &-footer {
+    position: absolute;
+    width: 50px;
+    bottom: 0;
+    right: 0;
+    padding: 5px;
+    color: #838eb4;
+    z-index: 99;
+    ul {
+      padding: 0;
+    }
+    li {
+      height: 40px;
+      width: 40px;
+      line-height: 40px;
+      text-align: center;
+      font-size: 30px;
+      cursor: pointer;
+      list-style-type: none;
+      transition: transform 0.2s linear;
+    }
+    li:hover {
+      text-shadow: 0 0 5px #333;
+      color: #e1efe0;
+      transform: scale(1.2, 1.2);
+    }
+    li.li-disabled {
+      cursor: not-allowed;
+      color: #d0d0d0 !important;
+      transform: none !important;
+    }
+  }
+
+  &-loading {
+    position: absolute;
+    width: 60px;
+    height: 60px;
+    top: 0;
+    left: 50%;
+    margin: 0 0 0 -30px;
+    color: rgba(221, 226, 237, 1);
+    font-size: 50px;
+    text-align: center;
+    line-height: 60px;
+    z-index: 99;
+  }
+
+  &-preload {
+    position: absolute;
+    z-index: 9;
+    width: 100px;
+    height: 100px;
+    overflow: hidden;
+    top: -1000px;
+    left: -1000px;
+  }
+
+  &-none {
+    position: absolute;
+    top: 50%;
+    left: 50%;
+    transform: translate(-50%, -50%);
+    color: #e0e0e0;
+    text-align: center;
+    font-size: 20px;
+    > i {
+      font-size: 30px;
+    }
+  }
+
+  // element
+  .el-dialog {
+    background: rgba(55, 55, 55, 0.8);
+  }
+  .el-dialog__body {
+    top: 0;
+    background-color: transparent;
+  }
+  .el-dialog__footer,
+  .el-dialog__header {
+    display: none;
+  }
+}