vue中-視頻上傳完整封裝復(fù)用組件

小魚兒心語:當(dāng)人生讓你碰壁頭破血流時(shí),別害怕,沒有這些挫折,怎能練就一身鋼筋鐵骨,當(dāng)生活給你一百個(gè)理由哭泣時(shí),別沮喪,你就拿出一千個(gè)理由笑給它看。
視頻的上傳.png

視頻的列表展示.png

視頻的修改回顯內(nèi)容展示.png

廢話不多說,直接上代碼:

視頻上傳uploadvideo.vue組件(直接復(fù)制即可):
<template>
  <div class="component-upload-image">
    <el-upload
      multiple
      :action="uploadImgUrl"
      :on-success="handleUploadSuccess"
      :before-upload="handleBeforeUpload"
      :limit="limit"
      :on-error="handleUploadError"
      :on-exceed="handleExceed"
      :on-progress="uploadVideoProcess"
      ref="imageUpload"
      :before-remove="handleDelete"
      :show-file-list="false"
      :headers="headers"
      :file-list="videoForm.showVideoPath"
      :on-preview="handlePictureCardPreview"
      :class="{ hide: videoForm.showVideoPath.length >= limit }"
      :disabled="isdisabled"
    >
    <el-button type="primary">上傳視頻</el-button>
      <!-- <video v-if="videoForm.showVideoPath !='' && !videoFlag"
      v-for="(item,index) in videoForm.showVideoPath"
      :key="index"
              v-bind:src="item"
              class="avatar video-avatar"
              controls="controls"
              style="width: 80%; height: 80%;">
          您的瀏覽器不支持視頻播放
      </video> -->
      <!-- <el-icon class="avatar-uploader-icon"><plus /></el-icon> -->
      <el-progress v-if="videoFlag == true"
                                 type="circle"
                                 v-bind:percentage="videoUploadPercent"
                                 style="margin-top:7px;"></el-progress>

      
    </el-upload>
    <!-- 自定義上傳文件列表 -->
    <div v-if="videoForm.showVideoPath.length > 0" style="width: 1300px;display: flex;flex-wrap: wrap;margin-top: 3%;">
      <div v-for="(item, index) in videoForm.showVideoPath">
        
        <!-- <img src="·······/視頻路徑" class="delete_icon" alt="" @click="deleteImg(item)"> -->
        <video v-if="videoForm.showVideoPath !='' && !videoFlag"
                v-bind:src="item.url"
                class="avatar video-avatar"
                controls="controls"
                style="width: 70%;">
            您的瀏覽器不支持視頻播放
        </video>
        <p style="width: 100%;height: 20px;cursor: pointer;" :style="{background:isbackground}" v-on:mouseover="handleMouseOver" v-on:mouseout="headleMouseOut">
          <span style="float: left;margin-top: -7px;">{{ item.name }}</span>
          <!-- <el-icon class="Close" style="float: right;"></el-icon> -->
          <el-icon :size="16" style="float: right;" @click="deleteImg(item,index)">
            <Close />
          </el-icon>
        </p>
      </div>
      </div>
    <!-- 上傳提示 -->
    <div class="el-upload__tip" v-if="showTip">
      請上傳
      <template v-if="fileSize">
        大小不超過 <b style="color: #f56c6c">{{ fileSize }}MB</b>
      </template>
      <template v-if="fileType">
        格式為 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
      </template>
      的文件
    </div>

    <el-dialog
      v-model="dialogVisible"
      title="預(yù)覽"
      width="800px"
      append-to-body
    >
      <img
        :src="dialogImageUrl"
        style="display: block; max-width: 100%; margin: 0 auto"
      />
    </el-dialog>
  </div>
</template>

<script setup>
import { getToken } from "@/utils/auth";

const props = defineProps({
  modelValue: [String, Object, Array],
  isdisabled: {
    type: Boolean,
    default: false
  },
  // 圖片數(shù)量限制
  limit: {
    type: Number,
    default: 5,
  },
  // 大小限制(MB)
  fileSize: {
    type: Number,
    default: 5,
  },
  // 文件類型, 例如['png', 'jpg', 'jpeg']
  fileType: {
    type: Array,
    default: () => ['video/mp4', 'video/ogg', 'video/flv', 'video/avi', 'video/wmv', 'video/rmvb', 'video/mov'],
  },
  // 是否顯示提示
  isShowTip: {
    type: Boolean,
    default: true
  },
});

const { proxy } = getCurrentInstance();
const emit = defineEmits();
const number = ref(0);
const uploadList = ref([]);
const dialogImageUrl = ref("");
const dialogVisible = ref(false);
const baseUrl = import.meta.env.VITE_APP_BASE_API;
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上傳的圖片服務(wù)器地址
const headers = ref({ Authorization: "Bearer " + getToken() });
const fileList = ref([]);
const showTip = computed(
  () => props.isShowTip && (props.fileType || props.fileSize)
);


const videoFlag = ref(false);
//是否顯示進(jìn)度條
const videoUploadPercent = ref("");
//進(jìn)度條的進(jìn)度
const isShowUploadVideo = ref(false);
//顯示上傳按鈕
const videoForm = ref({showVideoPath: []});

const isbackground = ref("");

watch(() => props.modelValue, val => {
  if (val) {
    // 首先將值轉(zhuǎn)為數(shù)組
    const list = Array.isArray(val) ? val : props.modelValue.split(",");
    // 然后將數(shù)組轉(zhuǎn)為對象數(shù)組
    videoForm.value.showVideoPath = list.map(item => {
      const filename = item.split('/') 
      const name = filename[filename.length-1]
      if (typeof item === "string") {
        if (item.indexOf(baseUrl) === -1) {
          item = { name: name, url: baseUrl + item };
        } else {
          item = { name: name, url: item };
        }
      }
      return item;
    });
  } else {
    videoForm.value.showVideoPath = [];
    return [];
  }
},{ deep: true, immediate: true });

function handleMouseOver(){
  isbackground.value = '#ede1e1'
}
function headleMouseOut(){
  isbackground.value = ''
}
function deleteImg(row,index){
  // videoForm.value.showVideoPath.forEach((item,index) => {
    videoForm.value.showVideoPath.splice(index, 1);
  // })
  
  // videoForm.value.showVideoPath = videoForm.value.showVideoPath.fileter(item => item.name == row.name)
}
// 上傳前l(fā)oading加載
function handleBeforeUpload(file) {
  // let isImg = false;
  // if (props.fileType.length) {
  //   let fileExtension = "";
  //   if (file.name.lastIndexOf(".") > -1) {
  //     fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
  //   }
  //   isImg = props.fileType.some(type => {
  //     if (file.type.indexOf(type) > -1) return true;
  //     if (fileExtension && fileExtension.indexOf(type) > -1) return true;
  //     return false;
  //   });
  // } else {
  //   isImg = file.type.indexOf("image") > -1;
  // }

  var fileSize = file.size / 1024 / 1024 < props.fileSize;
  if (['video/mp4', 'video/ogg', 'video/flv', 'video/avi', 'video/wmv', 'video/rmvb', 'video/mov'].indexOf(file.type) == -1) {
    proxy.$modal.msgError("請上傳正確的視頻格式");
      return false;
  }
  if (!fileSize) {
    proxy.$modal.msgError("視頻大小不能超過${props.fileSize}MB");
      return false;
  }
  isShowUploadVideo.value = false;
  // if (!isImg) {
  //   proxy.$modal.msgError(
  //     `文件格式不正確, 請上傳${props.fileType.join("/")}圖片格式文件!`
  //   );
  //   return false;
  // }
  // if (props.fileSize) {
  //   const isLt = file.size / 1024 / 1024 < props.fileSize;
  //   if (!isLt) {
  //     proxy.$modal.msgError(`上傳頭像圖片大小不能超過 ${props.fileSize} MB!`);
  //     return false;
  //   }
  // }
  // proxy.$modal.loading("正在上傳圖片,請稍候...");
  number.value++;
}
function uploadVideoProcess(event, file, fileList) {
    videoFlag.value = true;
    videoUploadPercent.value = file.percentage.toFixed(0) * 1;
}

// 文件個(gè)數(shù)超出
function handleExceed() {
  proxy.$modal.msgError(`上傳文件數(shù)量不能超過 ${props.limit} 個(gè)!`);
}

// 上傳成功回調(diào)
function handleUploadSuccess(res, file) {
  if (res.code === 200) {
    console.log(151,res)
    isShowUploadVideo.value = true;
    videoFlag.value = false;
    videoUploadPercent.value = 0;
    // uploadList.value.push({ name: res.fileName, url: res.fileName });
    // fileList.value.push({ name: res.fileName, url: res.fileName });
    videoForm.value.showVideoPath.push({ name: res.originalFilename, url: import.meta.env.VITE_APP_BASE_API + res.fileName });
    emit("file", listToString(videoForm.value.showVideoPath));
    uploadedSuccessfully();
  } else {
    number.value--;
    proxy.$modal.closeLoading();
    proxy.$modal.msgError(res.msg);
    proxy.$refs.imageUpload.handleRemove(file);
    uploadedSuccessfully();
  }
}

// 刪除圖片
function handleDelete(file) {
  console.log(166,fileList.value.map(f => f.name).toString(),file)
  // const findex = fileList.value.map(f => f.name).toString().indexOf(file.name);
  // console.log(168,findex)
  // if (findex > -1 && fileList.value.length == number.value) {
    if(fileList.value.length>0){
      fileList.value.forEach((item,index) => {
        console.log(172,item)
        if(item.url.indexOf(file.response.fileName)!=-1){
          fileList.value.splice(index, 1);
          console.log(170,fileList.value.length)
          emit("update:modelValue", listToString(fileList.value));
          emit("file", listToString(fileList.value));
          return false;
        }
      })
    }
    
  // }
}

// 上傳結(jié)束處理
function uploadedSuccessfully() {
  if (number.value > 0 && videoForm.value.showVideoPath.length === number.value) {
    // fileList.value = fileList.value.filter(f => f.url !== undefined).concat(uploadList.value);
    // fileList.value = uploadList.value
    // console.log(175,fileList.value,uploadList.value)
    // uploadList.value = [];
    // number.value = 0;
    emit("update:modelValue", listToString(videoForm.value.showVideoPath));
    emit("file", listToString(videoForm.value.showVideoPath));
    // proxy.$modal.closeLoading();
  }
}

// 上傳失敗
function handleUploadError() {
  proxy.$modal.msgError("上傳圖片失敗");
  proxy.$modal.closeLoading();
}

// 預(yù)覽
function handlePictureCardPreview(file) {
  dialogImageUrl.value = file.url;
  dialogVisible.value = true;
}

// 對象轉(zhuǎn)成指定字符串分隔
function listToString(list, separator) {
  let strs = "";
  separator = separator || ",";
  for (let i in list) {
    if (undefined !== list[i].url && list[i].url.indexOf("blob:") !== 0) {
      strs += list[i].url.replace(baseUrl, "") + separator;
    }
  }
  return strs != "" ? strs.substr(0, strs.length - 1) : "";
}
</script>

<style scoped lang="scss">
// .el-upload--picture-card 控制加號部分
:deep(.hide .el-upload--picture-card) {
    display: none;
}
</style>
父組件引用**完整流程**
<template>
  <div class="app-container">
    // 表格中視頻的展示
    <el-table v-loading="loading" :data="standardList" @selection-change="handleSelectionChange">
      <el-table-column label="視頻" align="center" prop="unsafeJzsp" width="100">
        <template #default="scope">
          <video :src="scope.row.unsafeJzsp"
              class="avatar video-avatar"
              controls="controls"
              style="width: 100%;">
           </video>
         </template>
      </el-table-column>
    </el-table>
    // 彈框中視頻上傳的組件引用
    // 添加或修改不安全行為標(biāo)準(zhǔn)對話框
    <el-dialog :title="title" v-model="open" width="50%" append-to-body>
      <el-form ref="standardRef" :model="form" :rules="rules" label-width="120px" inline>
        <el-form-item label="矯正視頻" prop="unsafeJzsp">
          // `modelValue`是修改時(shí)用于回顯視頻的屬性
          <uploadvideo v-model="form.unsafeJzsp" style="width: 970px;" @file="getvideo" :modelValue="form.unsafeJzsp"></uploadvideo>
        </el-form-item>
      </el-form>
      <template #footer>
        <div class="dialog-footer">
          <el-button type="primary" @click="submitForm">確 定</el-button>
          <el-button @click="cancel">取 消</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>

<script setup>
  // 接口為公共配置,引用即可,也可直接使用接口地址
  import { listStandard, getStandard, delStandard, addStandard, updateStandard } from "@/api/unsafe/standard";
  const { proxy } = getCurrentInstance();
  const standardList = ref([]);
  const data = reactive({
    form: {}
  });
  const { form } = toRefs(data);
  // 上傳視頻后視頻組件傳遞過來的方法,獲取上傳的視頻文件名
  function getvideo(e){
    form.value.unsafeJzsp = e
  }
  // 查詢不安全行為標(biāo)準(zhǔn)列表 
  function getList() {
    loading.value = true;
    listStandard().then(response => {
      standardList.value = response.rows;
      // 拼接視頻地址,import.meta.env.VITE_APP_BASE_API 為接口請求的ip地址,根據(jù)實(shí)際情況獲取~
      standardList.value.forEach(item => {
        item.unsafeJzsp = import.meta.env.VITE_APP_BASE_API + item.unsafeJzsp
      })
      loading.value = false;
    });
  }
  /** 修改按鈕操作 */
  function handleUpdate(row) {
    const _unsafeBzId = row.unsafeBzId || ids.value
    // 獲取詳情數(shù)據(jù)
    getStandard(_unsafeBzId).then(response => {
      form.value = response.data;
      // 拼接視頻地址,使提交的視頻正?;仫@展示
      form.value.unsafeJzsp = import.meta.env.VITE_APP_BASE_API + form.value.unsafeJzsp
      open.value = true;
      title.value = "修改不安全行為標(biāo)準(zhǔn)";
    });
  }
</script>

接下來給大家總結(jié)了video的標(biāo)簽,屬性,方法,事件的使用方法:

<video> 標(biāo)簽屬性:

src:視頻的URL
poster:視頻封面,沒有播放時(shí)顯示的圖片
preload:預(yù)加載
autoplay:自動播放
loop:循環(huán)播放
controls:瀏覽器自帶的控制條
width:視頻寬度
height:視頻高度

Html代碼:

<video ref=”media” src=”http://www.abc.com/test.mp4″ controls width=”400px” heigt=”400px”></video>  
// 獲取標(biāo)簽
const media= this.$refs.media;      ----vue2.0寫法
`或者`
const { proxy } = getCurrentInstance();
const media= proxy.$refs.media;     ----vue3.0寫法

Js代碼:
// 錯(cuò)誤狀態(tài)

Media.error; //null:正常
Media.error.code; //1.用戶終止 2.網(wǎng)絡(luò)錯(cuò)誤 3.解碼錯(cuò)誤 4.URL無效

// 網(wǎng)絡(luò)狀態(tài)

Media.currentSrc; //返回當(dāng)前資源的URL
Media.src = value; //返回或設(shè)置當(dāng)前資源的URL
Media.canPlayType(type); //是否能播放某種格式的資源
Media.networkState; //0.此元素未初始化 1.正常但沒有使用網(wǎng)絡(luò) 2.正在下載數(shù)據(jù) 3.沒有找到資源
Media.load(); //重新加載src指定的資源
Media.buffered; //返回已緩沖區(qū)域,TimeRanges
Media.preload; //none:不預(yù)載 metadata:預(yù)載資源信息 auto:

// 回放狀態(tài)

Media.currentTime = value; //當(dāng)前播放的位置,賦值可改變位置
Media.startTime; //一般為0,如果為流媒體或者不從0開始的資源,則不為0
Media.duration; //當(dāng)前資源長度 流返回?zé)o限
Media.paused; //是否暫停
Media.defaultPlaybackRate = value;//默認(rèn)的回放速度,可以設(shè)置
Media.playbackRate = value;//當(dāng)前播放速度,設(shè)置后馬上改變
Media.played; //返回已經(jīng)播放的區(qū)域,TimeRanges,關(guān)于此對象見下文
Media.seekable; //返回可以seek的區(qū)域 TimeRanges
Media.ended; //是否結(jié)束
Media.autoPlay; //是否自動播放
Media.loop; //是否循環(huán)播放
Media.play(); //播放
Media.pause(); //暫停

// 控制

Media.controls;//是否有默認(rèn)控制條
Media.volume = value; //音量
Media.muted = value; //靜音

事件:

eventTester = function(e){  
    Media.addEventListener(e,function(){  
        console.log((new Date()).getTime(),e);  
    });  
}  
  
eventTester(“l(fā)oadstart”);   //客戶端開始請求數(shù)據(jù)  
eventTester(“progress”);    //客戶端正在請求數(shù)據(jù)  
eventTester(“suspend”);     //延遲下載  
eventTester(“abort”);       //客戶端主動終止下載(不是因?yàn)殄e(cuò)誤引起),  
eventTester(“error”);       //請求數(shù)據(jù)時(shí)遇到錯(cuò)誤  
eventTester(“stalled”);     //網(wǎng)速失速  
eventTester(“play”);        //play()和autoplay開始播放時(shí)觸發(fā)  
eventTester(“pause”);       //pause()觸發(fā)  
eventTester(“l(fā)oadedmetadata”);  //成功獲取資源長度  
eventTester(“l(fā)oadeddata”);  //  
eventTester(“waiting”);     //等待數(shù)據(jù),并非錯(cuò)誤  
eventTester(“playing”);     //開始回放  
eventTester(“canplay”);     //可以播放,但中途可能因?yàn)榧虞d而暫停  
eventTester(“canplaythrough”); //可以播放,歌曲全部加載完畢  
eventTester(“seeking”);     //尋找中  
eventTester(“seeked”);      //尋找完畢  
eventTester(“timeupdate”);  //播放時(shí)間改變  
eventTester(“ended”);       //播放結(jié)束  
eventTester(“ratechange”);  //播放速率改變  
eventTester(“durationchange”);  //資源長度改變  
eventTester(“volumechange”);    //音量改變 

屬性列表:

屬性列表.png

媒介屬性: 一般用于js操作

媒介屬性.png

媒介屬性1.png

媒介屬性2.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容