Kaynağa Gözat

增加上传音频功能

cgp 1 gün önce
ebeveyn
işleme
3d33bcdf4a

+ 6 - 2
src/utils/hsy.js

@@ -27,13 +27,17 @@ export const uploadToHSY = async (file, onProgress, type, callBackUp) => {
     const datePath = `${yyyy}${MM}${dd}`;
 
 
-    const fileName = `${Date.now()}.mp4`;
+    const ext = (file.name || '').split('.').pop().toLowerCase() || 'mp4';
+    const fileName = `${Date.now()}.${ext}`;
 
     const remoteFileName = `course/${datePath}/${fileName}`;
 
+    // 火山云 VOD 的 FileType 只支持 video/image/object,音频按 video 类型上传
+    const mediaType = 'video'
+
     const fileKey = uploader.addFile({
       file,
-      type: 'video',
+      type: mediaType,
       fileName:remoteFileName,
       stsToken: {
         CurrentTime: credentials.currentTime,

+ 11 - 0
src/views/course/userCourseCategory/index.vue

@@ -90,6 +90,12 @@
           </div>
         </template>
       </el-table-column>
+      <el-table-column label="类型" align="center" prop="type">
+        <template slot-scope="scope">
+          <el-tag v-if="scope.row.type === 1" type="warning">音频</el-tag>
+          <el-tag v-else type="info">视频</el-tag>
+        </template>
+      </el-table-column>
       <el-table-column label="排序" sortable align="center" prop="sort" />
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template slot-scope="scope">
@@ -128,6 +134,10 @@
         <el-form-item label="分类名称" prop="cateName">
           <el-input v-model="form.cateName" placeholder="请输入分类名称" />
         </el-form-item>
+        <el-form-item label="类型" prop="type" v-if="form.pid == 0">
+          <el-radio v-model="form.type" label="0">视频</el-radio>
+          <el-radio v-model="form.type" label="1">音频</el-radio>
+        </el-form-item>
         <!-- <el-form-item label="图标"  prop="pic" v-if="form.pid!=0">
            <Material v-model="picArr" type="image" :num="1" :width="150" :height="150" />
         </el-form-item> -->
@@ -252,6 +262,7 @@ export default {
         cateName: null,
         sort: null,
         isShow: null,
+        type: null,
         createTime: null,
         updateTime: null,
         isDel: null

+ 34 - 122
src/views/course/videoResource/index.vue

@@ -246,7 +246,7 @@
           </el-select>
         </el-form-item>
 
-        <el-form-item label="上传视频" prop="videoUrl" required>
+        <el-form-item :label="currentCateType === 'audio' ? '上传音频' : '上传视频'" prop="videoUrl" required>
           <el-upload
             ref="videoUpload"
             action="#"
@@ -254,14 +254,14 @@
             :http-request="videoUpload"
             :file-list="fileList"
             :on-change="handleFileChange"
-            accept=".mp4">
+            :accept="currentCateType === 'audio' ? '.mp3,.m4a,audio/*' : '.mp4'">
             <i slot="default" class="el-icon-plus"></i>
             <div slot="file" slot-scope="{file}">
               <div
                 class="el-upload-list__item-thumbnail"
                 style="display: flex; justify-content: center; align-items: center; background-color: #f5f7fa; height: 146px;"
               >
-                <i class="el-icon-video-camera" style="font-size: 48px;" />
+                <i :class="currentCateType === 'audio' ? 'el-icon-microphone' : 'el-icon-video-camera'" style="font-size: 48px;" />
               </div>
 
               <span v-if="file.status === 'success'" class="el-upload-list__item-status-label">
@@ -326,6 +326,10 @@
           </el-upload>
         </el-form-item>
 
+        <el-form-item label="封面" prop="thumbnail" v-if="currentCateType === 'audio'">
+          <ImageUpload v-model="form.thumbnail" :limit="1" />
+        </el-form-item>
+
         <el-form-item label="时长">
           <span>{{ formatDuration(form.duration) }}</span>
         </el-form-item>
@@ -578,7 +582,7 @@
             </el-select>
           </el-form-item>
 
-          <el-form-item label="上传视频" prop="files">
+          <el-form-item :label="currentCateType === 'audio' ? '上传音频' : '上传视频'" prop="files">
             <el-upload
               ref="batchVideoUpload"
               action="#"
@@ -586,7 +590,7 @@
               :file-list="batchFileList"
               :on-change="handleBatchFileChange"
               multiple
-              accept=".mp4">
+              :accept="currentCateType === 'audio' ? '.mp3,.m4a,audio/*' : '.mp4'">
               <el-button type="primary" :disabled="batchUploadForm.typeSubId === null">选择文件</el-button>
               <div slot="tip" class="el-upload__tip">选择分类后才可以上传视频</div>
             </el-upload>
@@ -866,6 +870,8 @@ export default {
       typeList: [],
       rootTypeList: [],
       subTypeList: [],
+      // 当前选中一级分类类型:audio / video
+      currentCateType: 'video',
       // 视频上传
       videoDisabled: false,
       videoPreviewVisible: false,
@@ -1163,6 +1169,9 @@ export default {
         this.batchUploadForm.typeSubId = null
       }
       this.subTypeList = []
+      // 记录当前一级分类类型(1=音频 → audio,其余 → video)
+      const cur = this.rootTypeList.find(i => String(i.dictValue) === String(val))
+      this.currentCateType = cur && Number(cur.type) === 1 ? 'audio' : 'video'
       if (!val) {
         return
       }
@@ -1422,7 +1431,9 @@ export default {
       this.currentUploadProgress = { total: 0, line1: 0, line2: 0, line1Status: 'pending', line2Status: 'pending' };
 
       try {
-        await this.getFirstThumbnail(file, this.form);
+        if (this.currentCateType !== 'audio') {
+          await this.getFirstThumbnail(file, this.form);
+        }
         const [line1Result, line2Result] = await Promise.allSettled([
           this.uploadVideoToTxPcdn(file, this.form, options.onProgress),
           // this.uploadVideoToHwObs(file, this.form, options.onProgress)
@@ -1465,13 +1476,14 @@ export default {
     },
     // 获取媒体文件时长
     getMediaDuration(file) {
-      // 处理视频
-      const video = document.createElement('video');
-      video.preload = 'metadata';
-      video.onloadedmetadata = () => {
-        this.form.duration = Math.round(video.duration);
+      // 处理媒体(音频/视频)
+      const isAudio = this.currentCateType === 'audio'
+      const media = document.createElement(isAudio ? 'audio' : 'video');
+      media.preload = 'metadata';
+      media.onloadedmetadata = () => {
+        this.form.duration = Math.round(media.duration);
       };
-      video.src = URL.createObjectURL(file);
+      media.src = URL.createObjectURL(file);
     },
     handleFileChange(file, files) {
       // 保留最后一个文件
@@ -1767,16 +1779,16 @@ export default {
       this.uploadQueue.push(tempVideo);
       this.videoList.unshift(tempVideo);
 
-       // 获取视频时长
-      const video = document.createElement('video');
-      video.preload = 'metadata';
-      video.onloadedmetadata = () => {
+       // 获取媒体时长
+      const media = document.createElement(this.currentCateType === 'audio' ? 'audio' : 'video');
+      media.preload = 'metadata';
+      media.onloadedmetadata = () => {
         const index = this.videoList.findIndex(item => item.tempId === tempVideo.tempId);
         if (index !== -1) {
-          tempVideo.duration = Math.round(video.duration);
+          tempVideo.duration = Math.round(media.duration);
         }
       };
-      video.src = URL.createObjectURL(file);
+      media.src = URL.createObjectURL(file);
 
       // 关闭上传弹窗
       this.showUpload = false;
@@ -1836,8 +1848,10 @@ export default {
 
     async uploadSingleVideo(tempVideo) {
       try {
-        // 获取封面
-        await this.getFirstThumbnail(tempVideo.file, tempVideo);
+        // 获取封面(音频无首帧,跳过)
+        if (this.currentCateType !== 'audio') {
+          await this.getFirstThumbnail(tempVideo.file, tempVideo);
+        }
 
         // 并行上传到两个服务器
         const [line1Result, line2Result] = await Promise.allSettled([
@@ -2124,93 +2138,6 @@ export default {
       });
     },
 
-    /** 删除视频 */
-    handleDeleteVideo(row) {
-      if (row.uploadStatus === 'uploading') {
-        this.$confirm('该视频正在上传中,确认要取消上传并删除吗?', '取消上传', {
-          confirmButtonText: '确定取消',
-          cancelButtonText: '继续上传',
-          type: 'warning'
-        }).then(() => {
-          // Cancel the upload and remove from list
-          this.cancelVideoUpload(row);
-        }).catch(() => {
-          // User chose to continue uploading
-          this.$message.info('继续上传该视频');
-        });
-      } else {
-        // Original confirmation for non-uploading videos
-        this.$confirm('确认要从列表中删除该视频吗?', '提示', {
-          confirmButtonText: '确定',
-          cancelButtonText: '取消',
-          type: 'warning'
-        }).then(() => {
-          this.removeVideoFromList(row);
-        }).catch(() => {
-          // 取消删除
-        });
-      }
-    },
-
-    cancelVideoUpload(row) {
-      const cancellationTokens = this.uploadCancellationTokens.get(row.tempId);
-      if (cancellationTokens) {
-        // Cancel COS upload if exists
-        if (cancellationTokens.cos) {
-          try {
-            cancellationTokens.cos();
-            console.log('COS upload cancelled for video:', row.tempId);
-          } catch (error) {
-            console.error('Error cancelling COS upload:', error);
-          }
-        }
-
-        // Cancel OBS upload if exists
-        if (cancellationTokens.obs) {
-          try {
-            cancellationTokens.obs();
-            console.log('OBS upload cancelled for video:', row.tempId);
-          } catch (error) {
-            console.error('Error cancelling OBS upload:', error);
-          }
-        }
-
-        // Remove cancellation tokens
-        this.uploadCancellationTokens.delete(row.tempId);
-      }
-
-      const videoIndex = this.videoList.findIndex(item => item.tempId === row.tempId);
-      if (videoIndex !== -1) {
-        this.videoList[videoIndex].uploadStatus = 'cancelled';
-        this.videoList.splice(videoIndex, 1);
-      }
-
-      const queueIndex = this.uploadQueue.findIndex(item => item.tempId === row.tempId);
-      if (queueIndex !== -1) {
-        this.uploadQueue.splice(queueIndex, 1);
-        this.updateQueuePositions();
-      }
-
-      this.$message.success('已取消视频上传并从列表中移除');
-    },
-
-    removeVideoFromList(row) {
-      const videoIndex = this.videoList.findIndex(item => item.tempId === row.tempId);
-      if (videoIndex !== -1) {
-        this.videoList.splice(videoIndex, 1);
-      }
-
-      // Remove from upload queue if it's still queued
-      const queueIndex = this.uploadQueue.findIndex(item => item.tempId === row.tempId);
-      if (queueIndex !== -1) {
-        this.uploadQueue.splice(queueIndex, 1);
-        this.updateQueuePositions();
-        this.$message.success('已从上传队列中移除该视频');
-      } else {
-        this.$message.success('已从列表中移除该视频');
-      }
-    },
-
     getUploadStatusText(status, queuePosition) {
       switch (status) {
         case 'success':
@@ -2538,21 +2465,6 @@ export default {
         default: return '#909399';
       }
     },
-    // 获取上传状态文本
-    getUploadStatusText(status, queuePosition) {
-      switch (status) {
-        case 'success':
-          return '上传成功';
-        case 'failed':
-          return '上传失败';
-        case 'uploading':
-          return '上传中';
-        case 'queued':
-          return `队列中 (第${queuePosition}位)`;
-        default:
-          return '待上传';
-      }
-    },
     // 获取线路状态图标
     getLineStatusIcon(status) {
       switch (status) {