首页 > 其他分享 >ant-design-vue 视频上传组件封装处理

ant-design-vue 视频上传组件封装处理

时间:2023-03-09 20:59:10浏览次数:35  
标签:arr vue const fileList ant video file design response

/*
* 视频上传组件
*/
<template>
  <div>
    <a-upload
      listType="picture-card"
      :accept="acceptType"
      :action="uploadVideo"
      :fileList="fileList"
      :multiple="false"
      :disabled="disabled"
      @preview="handlePreview"
      :beforeUpload="beforeUpload"
      @change="event => handleChange(event, 'fileList')"
      v-decorator="['fileList', {rules:[{required: requiresUpload, message: '请上传视频!'}]}]"
    >
      <div v-if="fileList.length < limit"> <a-icon type="plus"/> </div>
    </a-upload>
    <a-modal :visible="previewVisible" :footer="null" @cancel="handleVideoCancel">
      <video alt="example" style="width: 100%" :src="videoUrl" controls>
        当前浏览器不支持 video 功能
      </video>
    </a-modal>
  </div>
</template>

<script>

  import { FILE_DISPLAY_PREFIX, parseFileRespon, UPLOAD_URL, getVideoFrameImage } from '@/api/upload'

  const uidGenerator = () => {
    return '-' + parseInt(Math.random() * 10000 + 1, 10)
  }
  const getFileName = (path) => {
    if (path.lastIndexOf('\\') >= 0) {
      const reg = new RegExp('\\\\', 'g')
      path = path.replace(reg, '/')
    }
    return path.substring(path.lastIndexOf('/') + 1)
  }
  export default {
    name: 'UploadVideo',
    props: {
      // 是否前端进行展示缩略图(前端获取缩略图处理)
      showThumbnail: {
        type: Boolean,
        default: false
      },
      // 上传文件数量限制, 默认1个
      limit: {
        type: Number,
        default: 1
      },
      // 上传文件大小限制, 单位Mb, 默认无限制
      fileSize: {
        type: Number,
        default: 0
      },
      // 是否必须上传验证
      requiresUpload: {
        type: Boolean,
        default: false
      },
      acceptType: {
        type: String,
        required: false,
        default: '.mp4, .3gp, .rmvb, .wmv, .flv, .avi'
      },
      text: {
        type: String,
        required: false,
        default: '点击上传'
      },
      // 初始视频内容
      value: {
        type: [String, Array],
        required: false,
        default: ''
      },
      // 组件是否能够上传, 默认支持上传
      disabled: {
        type: Boolean,
        default: false
      }
    },
    data () {
      return {
        fileDisplayPrefix: FILE_DISPLAY_PREFIX,
        parseFileRespon,
        // 上传地址
        uploadVideo: UPLOAD_URL + '/files',
        // 视频预览地址
        videoUrl: '',
        headers: {},
        fileList: [],
        fileLoading: false,
        previewVisible: false
      }
    },
    watch: {
      value (val) {
        // console.log('初始化视频资源路径', val)
        if (val instanceof Array) {
          this.initFileList(val.join(','))
        } else {
          this.initFileList(val)
        }
      }
    },
    created () {
      // const token = Vue.ls.get(ACCESS_TOKEN)
      // this.headers = { 'X-Access-Token': token }
    },

    methods: {
      initFileList (paths) {
        if (!paths || paths.length === 0) {
          this.fileList = []
          return
        }
        const arr = paths.split(',')
        if (this.showThumbnail) {
          for (let a = 0; a < arr.length; a++) {
            getVideoFrameImage(this.fileDisplayPrefix + arr[a]).then(res => {
              this.fileList.push({
                uid: uidGenerator(),
                name: getFileName(arr[a]),
                status: 'done',
                url: res,
                videoUrl: arr[a],
                response: {
                  code: 10000,
                  status: 'history',
                  message: arr[a],
                  result: [ arr[a] ]
                }
              })
            }).catch(e => {
              this.fileList.push({
                uid: uidGenerator(),
                name: getFileName(arr[a]),
                status: 'done',
                url: e,
                videoUrl: arr[a],
                response: {
                  code: 10000,
                  status: 'history',
                  message: arr[a],
                  result: [ arr[a] ]
                }
              })
            })
          }
        } else {
          for (let a = 0; a < arr.length; a++) {
            this.fileList.push({
              uid: uidGenerator(),
              name: getFileName(arr[a]),
              status: 'done',
              url: this.fileDisplayPrefix + arr[a],
              videoUrl: arr[a],
              response: {
                code: 10000,
                status: 'history',
                message: arr[a],
                result: [ arr[a] ]
              }
            })
          }
        }
        // console.log('sss', fileList)
        // this.fileList = fileList
      },
      handlePathChange () {
        const uploadFiles = this.fileList
        let path = ''
        if (!uploadFiles || uploadFiles.length === 0) {
          path = ''
        }
        const arr = []

        for (var a = 0; a < uploadFiles.length; a++) {
          if (uploadFiles[a].response) {
            arr.push(uploadFiles[a].response.message)
          }
        }
        if (arr.length > 0) {
          path = arr.join(',')
        }
        this.$emit('change', path)
      },
      // 图片上传前的校验
      beforeUpload (file) {
        return true
      },
      handleChange (info, listStr) {
        if (info.file.status) {
          this[listStr] = info.fileList
        }
        if (info.file.status === 'uploading') {
          this.fileLoading = true
        } else if (info.file.status === 'error') {
          this.$message.error(this.$t('服务器无响应,请联系管理员!'))
          this.fileLoading = false
        } else if (info.file.status === 'done') {
          if (info.file.response.code === 10000) {
            const that = this
            // 缩略图处理
            this[listStr][info.fileList.length - 1].url = this.fileDisplayPrefix + this.parseFileRespon(info.file.response) || ''
            this[listStr][info.fileList.length - 1].videoUrl = this.parseFileRespon(info.file.response) || ''
            // 前端缩略图处理
            if (this.showThumbnail) {
              getVideoFrameImage(that.fileList[that.fileList.length - 1].url).then(res => {
                that.fileList[that.fileList.length - 1].url = res
                that.$forceUpdate()
              })
            }
          } else {
            this.fileList.pop()
            this.$message.error('上传视频失败!')
          }
        } else if (info.file.status === 'removed') {
          this.$emit('change', this.fileList.map(i => i.videoUrl))
        }
        this.$forceUpdate()
      },
      handleVideoCancel () {
        this.previewVisible = false
        const myVideo = document.getElementsByTagName('video')
        const arr = Array.from(myVideo)
        // 关闭时暂停播放
        arr.forEach(item => {
          item.pause()
        })
      },
      // 视频文件预览
      async handlePreview (file) {
        this.videoUrl = this.fileDisplayPrefix + file.videoUrl
        // console.log('路径', this.videoUrl)
        this.previewVisible = true
      },
      handleDelete (file) {
        // 如有需要新增 删除逻辑
        console.log(file)
      },
      // 获取上传的视频地址, 以,分割
      getUrls () {
        let urls = ''
        if (this.fileList.length > 0) {
          this.fileList.forEach(item => {
            if (urls === '') {
              urls = item.response ? this.parseFileRespon(item.response) : item.videoUrl.replaceAll('/uploads', '')
            } else {
              const urlPath = item.response ? this.parseFileRespon(item.response) : item.videoUrl.replaceAll('/uploads', '')
              urls += ',' + urlPath
            }
          })
        }
        return urls
      },
      // 获取视频标题+时长+缩略图+宽高
      asyncImgChecked (file) {
        return new Promise((resolve, reject) => {
          const reader = new FileReader()
          // 必须用file.raw
          reader.readAsDataURL(file)
          // reader.readAsDataURL(file.raw)
          reader.onload = () => {
            // 让页面中的img标签的src指向读取的路径
            const video = document.createElement('video')
            video.src = reader.result
            video.currentTime = 3 // 截取缩略图时的视频时长,一定要设置,不然大概率白屏
            video.oncanplay = () => {
              const canvas = document.createElement('canvas')
              const ctx = canvas.getContext('2d')
              canvas.setAttribute('width', 100)
              canvas.setAttribute('height', 100)
              video.setAttribute('width', 100)
              video.setAttribute('height', 100)
              ctx.drawImage(video, 0, 0, video.width, video.height)
              const imgSrc = canvas.toDataURL('image/png')
              resolve({
                // base64的缩略图图片路径
                thumbnail: imgSrc,
                width: video.videoWidth,
                height: video.videoHeight,
                videoName: file.name
              })
            }
          }
        })
      }
    }
  }
</script>

<style scoped>

</style>

 

附录upload.js内容

import path from './index'
import { isEmpty } from '@/utils/common'
import { axios } from '@/utils/request'
// 上传文件地址
export const UPLOAD_URL = process.env.VUE_APP_API_BASE_URL + path.upload
// 文件回显前缀
export const FILE_DISPLAY_PREFIX = path.uploads

// 解析文件响应
export function parseFileRespon (response) {
  if (isEmpty(response) || response.code !== 10000 || response.result.length < 1) {
    this.$message.error(() => {
      return response.msg || '服务异常,请稍后再试'
    })
    return ''
  } else {
    return response.result[0]
  }
}

/**
 * 1分片上传 地址
 * @returns
 */
export function chunk () {
  return UPLOAD_URL + '/oss/aliyun/chunk'
}

/**
 * 2分片上传完成
 * @param identifier
 * @returns {AxiosPromise}
 */
export function chunkComplete (identifier) {
  return axios({
    url: path.upload + '/oss/aliyun/chunk/complete',
    method: 'post',
    params: { 'identifier': identifier }
  })
}

/**
 * 3分片上传取消
 * @param identifier
 * @returns {AxiosPromise}
 */
export function chunkAbort (identifier) {
  return axios({
    url: path.upload + '/oss/aliyun/chunk/abort',
    method: 'post',
    params: { 'identifier': identifier }
  })
}
/**
 * 自定义上传图片
 * @param file
 * @returns {AxiosPromise}
 */
export function customUploadFile (file) {
  const data = new FormData()
  data.append('file', file)
  return axios({
    url: path.upload + '/files',
    method: 'POST',
    headers: { 'Content-Type': 'multipart/form-data' },
    timeout: 1800000,
    data: data
  })
}

/**
 * 获取缩略图
 * @param url 地址
 * @param currentTime 缩略图取第几秒的图片
 * @param width
 * @param height
 * @returns {Promise<unknown>}
 */
export function getVideoFrameImage (url, currentTime = 3, width = 100, height = 100) {
  return new Promise((resolve, reject) => {
    const video = document.createElement('video')
    video.setAttribute('crossOrigin', 'anonymous')
    video.setAttribute('preload', 'metadata')
    video.setAttribute('src', url)
    // 视频开始可能是黑屏状态
    video.currentTime = currentTime
    video.addEventListener('loadeddata', function () {
      const canvas = document.createElement('canvas')
      const { videoWidth, videoHeight } = video
      const newHeight = height || videoHeight * (width / videoWidth)
      canvas.width = width
      canvas.height = newHeight
      canvas.getContext('2d').drawImage(video, 0, 0, 200, height)
      resolve(canvas.toDataURL('image/jpeg'))
    })
    // 错误时的特殊处理,(资源404或不可用时触发)
    video.addEventListener('error', function (e) {
      // console.log('ss', e)
      reject(url)
    })
  })
}

 

标签:arr,vue,const,fileList,ant,video,file,design,response
From: https://www.cnblogs.com/qiushuiyu-108/p/17197330.html

相关文章