|
|
@@ -88,6 +88,15 @@
|
|
|
@click="handleBatchUpdateCategory"
|
|
|
>批量修改分类</el-button>
|
|
|
</el-col>
|
|
|
+ <el-col :span="1.5">
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ plain
|
|
|
+ icon="el-icon-upload"
|
|
|
+ size="mini"
|
|
|
+ @click="handleBatchUpload"
|
|
|
+ >批量上传视频</el-button>
|
|
|
+ </el-col>
|
|
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
</el-row>
|
|
|
|
|
|
@@ -136,6 +145,12 @@
|
|
|
icon="el-icon-edit"
|
|
|
@click="handleUpdateCompanyIds(scope.row)"
|
|
|
>修改公司范围</el-button>
|
|
|
+ <el-button
|
|
|
+ size="mini"
|
|
|
+ type="text"
|
|
|
+ icon="el-icon-upload"
|
|
|
+ @click="handleReuploadVideo(scope.row)"
|
|
|
+ >再次上传视频</el-button>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
@@ -323,13 +338,91 @@
|
|
|
<el-button @click="cancelBatchUpdateCategory">取 消</el-button>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
+
|
|
|
+ <!-- 批量上传视频对话框 -->
|
|
|
+ <el-dialog title="批量上传视频" :visible.sync="batchUploadDialog" width="900px" append-to-body>
|
|
|
+ <div style="margin-bottom: 15px;">
|
|
|
+ <el-upload
|
|
|
+ ref="batchUpload"
|
|
|
+ action="#"
|
|
|
+ :http-request="handleBatchUploadRequest"
|
|
|
+ accept=".mp4"
|
|
|
+ :multiple="true"
|
|
|
+ :auto-upload="false"
|
|
|
+ :on-change="handleBatchFileChange"
|
|
|
+ :on-remove="handleBatchFileRemove"
|
|
|
+ :file-list="batchUploadFileList"
|
|
|
+ >
|
|
|
+ <el-button slot="trigger" size="small" type="primary">选择视频文件</el-button>
|
|
|
+ <el-button style="margin-left: 10px;" size="small" type="success" @click="submitBatchUpload">开始上传</el-button>
|
|
|
+ <div slot="tip" class="el-upload__tip">只能上传mp4文件,且每个文件不超过1G,可同时选择多个文件</div>
|
|
|
+ </el-upload>
|
|
|
+ </div>
|
|
|
+ <div v-if="batchUploadVideos.length > 0" style="margin-top: 20px;">
|
|
|
+ <div v-for="(video, index) in batchUploadVideos" :key="index" style="margin-bottom: 20px; border: 1px solid #e4e7ed; padding: 15px; border-radius: 4px;">
|
|
|
+ <div style="margin-bottom: 10px; font-weight: bold;">{{ video.fileName }}</div>
|
|
|
+ <div class="progress-container" style="width:100% !important;">
|
|
|
+ <span class="progress-label">上传进度</span>
|
|
|
+ <el-progress
|
|
|
+ style="margin-top: 5px;"
|
|
|
+ class="progress"
|
|
|
+ :text-inside="true"
|
|
|
+ :stroke-width="18"
|
|
|
+ :percentage="video.progress"
|
|
|
+ :status="video.status"
|
|
|
+ ></el-progress>
|
|
|
+ </div>
|
|
|
+ <div v-if="video.status === 'success'" style="margin-top: 10px; color: #67c23a; font-size: 12px;">
|
|
|
+ 上传成功
|
|
|
+ </div>
|
|
|
+ <div v-else-if="video.status === 'exception'" style="margin-top: 10px; color: #f56c6c; font-size: 12px;">
|
|
|
+ 上传失败: {{ video.errorMsg || '未知错误' }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="saveBatchUploadVideos" :disabled="!canSaveBatchUpload">保 存</el-button>
|
|
|
+ <el-button @click="cancelBatchUpload">取 消</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 重新上传视频对话框 -->
|
|
|
+ <el-dialog title="重新上传视频" :visible.sync="reuploadVideoDialog" width="800px" append-to-body>
|
|
|
+ <el-form ref="reuploadVideoForm" :model="reuploadVideoForm" label-width="120px">
|
|
|
+ <el-form-item label="视频名称">
|
|
|
+ <el-input v-model="reuploadVideoForm.videoName" :disabled="true" />
|
|
|
+ </el-form-item>
|
|
|
+ <live-video-upload
|
|
|
+ :type="1"
|
|
|
+ :isPrivate="isPrivate"
|
|
|
+ :fileKey.sync="reuploadVideoForm.fileKey"
|
|
|
+ :fileSize.sync="reuploadVideoForm.fileSize"
|
|
|
+ :videoUrl.sync="reuploadVideoForm.videoUrl"
|
|
|
+ :fileName.sync="reuploadVideoForm.fileName"
|
|
|
+ :line_1.sync="reuploadVideoForm.lineOne"
|
|
|
+ :thumbnail.sync="reuploadVideoForm.thumbnail"
|
|
|
+ :uploadType.sync="reuploadVideoForm.uploadType"
|
|
|
+ :isTranscode.sync="reuploadVideoForm.isTranscode"
|
|
|
+ :transcodeFileKey.sync="reuploadVideoForm.transcodeFileKey"
|
|
|
+ @video-duration="handleReuploadVideoDuration"
|
|
|
+ @change="handleReuploadVideoChange"
|
|
|
+ ref="reuploadVideoUpload"
|
|
|
+ append-to-body
|
|
|
+ />
|
|
|
+ </el-form>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="submitReuploadVideo">确 定</el-button>
|
|
|
+ <el-button @click="cancelReuploadVideo">取 消</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { listLiveVideo, getLiveVideo, delLiveVideo, addLiveVideo, updateLiveVideo, exportLiveVideo, batchUpdateCategory } from "@/api/live/liveVideo";
|
|
|
+import { listLiveVideo, getLiveVideo, delLiveVideo, addLiveVideo, updateLiveVideo, exportLiveVideo, batchUpdateCategory, updateVideoUrl, batchAddLiveVideo } from "@/api/live/liveVideo";
|
|
|
import { getCompanyList } from "@/api/company/company";
|
|
|
import LiveVideoUpload from "@/components/LiveVideoUpload/index.vue";
|
|
|
+import { uploadObject } from "@/utils/cos.js";
|
|
|
|
|
|
export default {
|
|
|
name: "LiveVideo",
|
|
|
@@ -414,7 +507,29 @@ export default {
|
|
|
// 所有公司选项(用于下拉选择)
|
|
|
allCompanyOptions: [],
|
|
|
// 公司选项加载状态
|
|
|
- companyOptionsLoading: false
|
|
|
+ companyOptionsLoading: false,
|
|
|
+ // 是否显示批量上传对话框
|
|
|
+ batchUploadDialog: false,
|
|
|
+ // 批量上传视频数据
|
|
|
+ batchUploadVideos: [],
|
|
|
+ // 批量上传文件列表
|
|
|
+ batchUploadFileList: [],
|
|
|
+ // 是否显示重新上传视频对话框
|
|
|
+ reuploadVideoDialog: false,
|
|
|
+ // 重新上传视频表单
|
|
|
+ reuploadVideoForm: {
|
|
|
+ videoId: null,
|
|
|
+ videoName: '',
|
|
|
+ videoUrl: '',
|
|
|
+ fileKey: '',
|
|
|
+ fileSize: null,
|
|
|
+ fileName: '',
|
|
|
+ lineOne: '',
|
|
|
+ thumbnail: '',
|
|
|
+ uploadType: 1,
|
|
|
+ isTranscode: 0,
|
|
|
+ transcodeFileKey: null
|
|
|
+ }
|
|
|
};
|
|
|
},
|
|
|
created() {
|
|
|
@@ -823,6 +938,237 @@ export default {
|
|
|
}).catch(() => {
|
|
|
this.msgError("批量修改失败");
|
|
|
});
|
|
|
+ },
|
|
|
+ /** 打开批量上传视频对话框 */
|
|
|
+ handleBatchUpload() {
|
|
|
+ this.batchUploadFileList = [];
|
|
|
+ this.batchUploadVideos = [];
|
|
|
+ this.batchUploadDialog = true;
|
|
|
+ },
|
|
|
+ /** 取消批量上传 */
|
|
|
+ cancelBatchUpload() {
|
|
|
+ this.batchUploadDialog = false;
|
|
|
+ this.batchUploadFileList = [];
|
|
|
+ this.batchUploadVideos = [];
|
|
|
+ if (this.$refs.batchUpload) {
|
|
|
+ this.$refs.batchUpload.clearFiles();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /** 批量文件变化 */
|
|
|
+ handleBatchFileChange(file, fileList) {
|
|
|
+ const MAX_SIZE = 1024 * 1024 * 1024;
|
|
|
+ if (file.raw && file.raw.size > MAX_SIZE) {
|
|
|
+ this.$message.error(`${file.name} 文件大小不能超过1G`);
|
|
|
+ // 从文件列表中移除
|
|
|
+ const index = fileList.findIndex(f => f.uid === file.uid);
|
|
|
+ if (index > -1) {
|
|
|
+ fileList.splice(index, 1);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.batchUploadFileList = fileList;
|
|
|
+
|
|
|
+ // 初始化上传数据
|
|
|
+ const index = this.batchUploadVideos.length;
|
|
|
+ this.batchUploadVideos.push({
|
|
|
+ file: file.raw,
|
|
|
+ fileName: file.name,
|
|
|
+ fileSize: file.size,
|
|
|
+ videoUrl: '',
|
|
|
+ fileKey: '',
|
|
|
+ duration: 0,
|
|
|
+ progress: 0,
|
|
|
+ status: '',
|
|
|
+ errorMsg: ''
|
|
|
+ });
|
|
|
+
|
|
|
+ // 获取视频时长
|
|
|
+ this.getBatchVideoDuration(file.raw, index);
|
|
|
+ },
|
|
|
+ /** 批量文件移除 */
|
|
|
+ handleBatchFileRemove(file, fileList) {
|
|
|
+ this.batchUploadFileList = fileList;
|
|
|
+ const index = this.batchUploadVideos.findIndex(v => v.fileName === file.name);
|
|
|
+ if (index > -1) {
|
|
|
+ this.batchUploadVideos.splice(index, 1);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /** 获取批量视频时长 */
|
|
|
+ getBatchVideoDuration(file, index) {
|
|
|
+ const video = document.createElement("video");
|
|
|
+ video.preload = "metadata";
|
|
|
+ video.onloadedmetadata = () => {
|
|
|
+ window.URL.revokeObjectURL(video.src);
|
|
|
+ const duration = parseInt(video.duration.toFixed(2));
|
|
|
+ if (this.batchUploadVideos[index]) {
|
|
|
+ this.batchUploadVideos[index].duration = duration;
|
|
|
+ }
|
|
|
+ };
|
|
|
+ video.src = URL.createObjectURL(file);
|
|
|
+ },
|
|
|
+ /** 批量上传请求处理 */
|
|
|
+ handleBatchUploadRequest(options) {
|
|
|
+ // 这个方法不会被调用,因为我们使用auto-upload=false
|
|
|
+ },
|
|
|
+ /** 开始批量上传 */
|
|
|
+ async submitBatchUpload() {
|
|
|
+ if (this.batchUploadVideos.length === 0) {
|
|
|
+ this.msgWarning("请先选择视频文件");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 重置所有视频状态
|
|
|
+ this.batchUploadVideos.forEach(video => {
|
|
|
+ video.progress = 0;
|
|
|
+ video.status = '';
|
|
|
+ video.errorMsg = '';
|
|
|
+ });
|
|
|
+
|
|
|
+ // 逐个上传视频
|
|
|
+ for (let i = 0; i < this.batchUploadVideos.length; i++) {
|
|
|
+ const video = this.batchUploadVideos[i];
|
|
|
+ try {
|
|
|
+ video.status = '';
|
|
|
+ video.progress = 0;
|
|
|
+
|
|
|
+ // 上传到COS
|
|
|
+ const updateProgress = (progressData) => {
|
|
|
+ video.progress = Math.round(progressData.percent * 100);
|
|
|
+ };
|
|
|
+
|
|
|
+ const data = await uploadObject(video.file, updateProgress, 1);
|
|
|
+
|
|
|
+ // 构建视频URL
|
|
|
+ let line_1 = `${process.env.VUE_APP_VIDEO_LINE_1}${data.urlPath}`;
|
|
|
+ line_1 = line_1.replace(/\.mp4$/, '.m3u8');
|
|
|
+
|
|
|
+ video.videoUrl = line_1;
|
|
|
+ video.fileKey = data.urlPath.substring(1);
|
|
|
+ video.progress = 100;
|
|
|
+ video.status = 'success';
|
|
|
+ } catch (error) {
|
|
|
+ console.error(`视频 ${video.fileName} 上传失败:`, error);
|
|
|
+ video.status = 'exception';
|
|
|
+ video.errorMsg = error.message || '上传失败';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /** 计算是否可以保存 */
|
|
|
+ canSaveBatchUpload() {
|
|
|
+ if (this.batchUploadVideos.length === 0) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 检查是否所有视频都已上传成功
|
|
|
+ return this.batchUploadVideos.every(video => video.status === 'success' && video.videoUrl);
|
|
|
+ },
|
|
|
+ /** 保存批量上传的视频 */
|
|
|
+ saveBatchUploadVideos() {
|
|
|
+ if (!this.canSaveBatchUpload) {
|
|
|
+ this.msgWarning("请等待所有视频上传完成");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建保存数据
|
|
|
+ const videoList = this.batchUploadVideos.map(video => {
|
|
|
+ return {
|
|
|
+ videoUrl: video.videoUrl,
|
|
|
+ videoName: video.fileName.replace(/\.[^/.]+$/, ""), // 去掉扩展名
|
|
|
+ videoType: -1, // 视频库
|
|
|
+ liveId: -1,
|
|
|
+ fileSize: video.fileSize,
|
|
|
+ duration: video.duration,
|
|
|
+ finishStatus: 0
|
|
|
+ };
|
|
|
+ });
|
|
|
+
|
|
|
+ // 调用批量新增接口
|
|
|
+ batchAddLiveVideo(videoList).then(response => {
|
|
|
+ if (response.code === 200) {
|
|
|
+ this.msgSuccess("批量保存成功");
|
|
|
+ this.batchUploadDialog = false;
|
|
|
+ this.batchUploadFileList = [];
|
|
|
+ this.batchUploadVideos = [];
|
|
|
+ this.getList();
|
|
|
+ } else {
|
|
|
+ this.msgError(response.msg || "批量保存失败");
|
|
|
+ }
|
|
|
+ }).catch(() => {
|
|
|
+ this.msgError("批量保存失败");
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /** 打开重新上传视频对话框 */
|
|
|
+ handleReuploadVideo(row) {
|
|
|
+ this.reuploadVideoForm = {
|
|
|
+ videoId: row.videoId,
|
|
|
+ videoName: row.videoName || '',
|
|
|
+ videoUrl: '',
|
|
|
+ fileKey: '',
|
|
|
+ fileSize: null,
|
|
|
+ fileName: '',
|
|
|
+ lineOne: '',
|
|
|
+ thumbnail: '',
|
|
|
+ uploadType: 1,
|
|
|
+ isTranscode: 0,
|
|
|
+ transcodeFileKey: null,
|
|
|
+ duration: 0
|
|
|
+ };
|
|
|
+ this.reuploadVideoDialog = true;
|
|
|
+ // 重置上传组件
|
|
|
+ this.$nextTick(() => {
|
|
|
+ if (this.$refs.reuploadVideoUpload) {
|
|
|
+ this.$refs.reuploadVideoUpload.reset();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /** 取消重新上传视频 */
|
|
|
+ cancelReuploadVideo() {
|
|
|
+ this.reuploadVideoDialog = false;
|
|
|
+ this.reuploadVideoForm = {
|
|
|
+ videoId: null,
|
|
|
+ videoName: '',
|
|
|
+ videoUrl: '',
|
|
|
+ fileKey: '',
|
|
|
+ fileSize: null,
|
|
|
+ fileName: '',
|
|
|
+ lineOne: '',
|
|
|
+ thumbnail: '',
|
|
|
+ uploadType: 1,
|
|
|
+ isTranscode: 0,
|
|
|
+ transcodeFileKey: null,
|
|
|
+ duration: 0
|
|
|
+ };
|
|
|
+ },
|
|
|
+ /** 重新上传视频时长回调 */
|
|
|
+ handleReuploadVideoDuration(duration) {
|
|
|
+ this.reuploadVideoForm.duration = duration;
|
|
|
+ },
|
|
|
+ /** 重新上传视频URL变化回调 */
|
|
|
+ handleReuploadVideoChange(videoUrl) {
|
|
|
+ this.reuploadVideoForm.videoUrl = videoUrl;
|
|
|
+ },
|
|
|
+ /** 提交重新上传视频 */
|
|
|
+ submitReuploadVideo() {
|
|
|
+ if (!this.reuploadVideoForm.videoUrl) {
|
|
|
+ this.msgError("请上传视频");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const videoUrl = this.reuploadVideoForm.videoUrl.replace('.mp4', '.m3u8');
|
|
|
+ updateVideoUrl({
|
|
|
+ videoId: this.reuploadVideoForm.videoId,
|
|
|
+ videoUrl: videoUrl
|
|
|
+ }).then(response => {
|
|
|
+ if (response.code === 200) {
|
|
|
+ this.msgSuccess("上传成功");
|
|
|
+ this.reuploadVideoDialog = false;
|
|
|
+ this.getList();
|
|
|
+ this.cancelReuploadVideo();
|
|
|
+ } else {
|
|
|
+ this.msgError(response.msg || "上传失败");
|
|
|
+ }
|
|
|
+ }).catch(() => {
|
|
|
+ this.msgError("上传失败");
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
@@ -834,4 +1180,15 @@ export default {
|
|
|
height: 100%;
|
|
|
object-fit: cover;
|
|
|
}
|
|
|
+
|
|
|
+.progress-container {
|
|
|
+ margin-bottom: 5px;
|
|
|
+}
|
|
|
+
|
|
|
+.progress-label {
|
|
|
+ display: block;
|
|
|
+ font-weight: bold;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #303331;
|
|
|
+}
|
|
|
</style>
|