SOURCE

<template>
  <div
    class="ue-media-preview"
    :class="[`ue-media-preview-${type}`]"
    :style="getMaskBg"
    v-show="visible"
  >
    <transition>
      <div
        class="ue-media-preview-warp"
        data-ue-loading-spinner-type="snake"
        data-ue-loading-color="rgb(94, 129, 244)"
        data-ue-loading-mask-color="rgba(0, 0, 0, 0)"
        data-ue-loading-spinner-color="rgb(94, 129, 244)"
        data-ue-loading-text="加载中......"
        v-ue-loading="loading"
      >
        <template v-if="type=== 'image'">
          <img :src="url" @error="imgError" @load="imgLoad" :style="getStyle()" />
        </template>
        <template v-if="type=== 'video'">
          <video
            ref="video"
            height="100%"
            controls="false"
            :autoplay="autoplay"
            loop="loop"
            :muted="muted"
            preload="metadata"
            :src="url"
            :class="{'loading':loading}"
          ></video>
        </template>
        <i class="ue-media-preview-close el-icon-circle-close" @click="doDestory" v-if="!loading"></i>
      </div>
    </transition>
  </div>
</template>

<script>
/*
  **************************************************
  version: '0.10'
  author": "46898"
  description": 媒体预览组件
  **************************************************
 */
export default {
  name: 'ueMediaPreview',
  model: {
    prop: 'val',
    event: 'update'
  },
  props: {
    val: {
      type: Boolean,
      default: false
    },
    type: {
      type: String,
      default: 'image',
      validator (value) {
        return [
          'image',
          'video'
        ].indexOf(value) > -1
      }
    },
    maskColor: {
      type: String,
      default: 'rgba(0, 0, 0, .5)'
    },
    url: {
      type: String,
      require: true
    },
    posterUrl: {
      type: String
    },
    autoplay: {
      type: Boolean,
      value: false
    }
  },
  data () {
    return {
      visible: this.val,
      muted: false,
      loading: true
    }
  },
  computed: {
    // 遮罩层背景
    getMaskBg () {
      const { maskColor } = this
      return {
        backgroundColor: maskColor
      }
    }
  },
  watch: {
    val (newVal) {
      this.visible = newVal
    }
  },
  methods: {
    getStyle () {
      return {
        'opacity': this.loading ? 0 : 1,
        'max-height': document.documentElement.clientHeight * 0.8 + 'px'
      }
    },
    imgLoad (e) {
      this.loading = false
    },
    imgError (e) {
      this.imgLoad(e)
      this.doDestory()
      this.$message.warning('预览失败')
    },
    videoDone (e) {
      this.loading = false
    },
    videoError (e) {
      this.videoDone(e)
      this.doDestory()
      this.$message.warning('预览失败')
    },
    // 销毁事件
    doDestory () {
      this.updateStatus(false)
      window.URL.revokeObjectURL(this.url)
      this.$destroy(true)
      if (this.$el.parentNode) {
        this.$el.parentNode.removeChild(this.$el)
      }
    },
    // 更新状态
    updateStatus (status = true) {
      this.visible = status
      this.$emit('update', status)
    },
    // 视频监听事件:加载完和加载失败
    addVideoEvent () {
      this.$nextTick(() => {
        const { type, videoDone, videoError } = this
        if (type === 'video') {
          const _video = this.$refs.video
          _video.addEventListener('canplaythrough', videoDone)
          _video.addEventListener('error', videoError)
          this.$once('hook:beforeDestroy', () => {
            if (_video) {
              _video.removeEventListener('canplaythrough', videoDone)
              _video.removeEventListener('error', videoError)
            }
          })
        }
      })
    }
  },
  mounted () {
    this.addVideoEvent()
  }
}
</script>

<style lang="scss">
.ue-media-preview {
  position: fixed;
  left: 0;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: 10009;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  .ue-media-preview-warp {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 70px;
    .ue-loading {
      width: 100%;
      .ue-loading-warp {
        padding: 0;
      }
    }
  }
  .ue-media-preview-close {
    position: absolute;
    right: -40px;
    top: -30px;
    color: #fff;
    font-size: 28px;
    font-weight: normal;
    cursor: pointer;
  }
  &.ue-media-preview-image {
    .ue-media-preview-warp {
      img {
        -webkit-user-select: none;
        -moz-user-select: none;
        -khtml-user-select: none;
        -ms-user-select: none;
        -o-user-select: none;
        user-select: none;
        border-radius: 8px;
        overflow: hidden;
      }
    }
  }
  &.ue-media-preview-video {
    .ue-media-preview-warp {
      height: 70%;
      video {
        border-radius: 8px;
        overflow: hidden;
        object-fit: fill;
        background: #000;
        &.loading {
          opacity: 0;
          background: transparent;
        }
      }
    }
  }
}
</style>
console 命令行工具 X clear

                    
>
console