From 044d520a74256e435e55f215060bf06bc7442d7f Mon Sep 17 00:00:00 2001 From: 康鲁杰 <60095866+KangLujie@users.noreply.github.com> Date: 星期五, 18 四月 2025 14:39:26 +0800 Subject: [PATCH] 我的视频选择片头片尾 --- easegen-front/src/views/myCourse/index.vue | 354 +++++++++++++++++++++++++++++++++++++++------------------- 1 files changed, 239 insertions(+), 115 deletions(-) diff --git a/easegen-front/src/views/myCourse/index.vue b/easegen-front/src/views/myCourse/index.vue index 9a4fe19..83b4e7c 100644 --- a/easegen-front/src/views/myCourse/index.vue +++ b/easegen-front/src/views/myCourse/index.vue @@ -147,7 +147,22 @@ </ContentWrap> <!-- 瑙嗛鎾斁寮规 --> - <videoDialog ref="videoRef" /> + <el-dialog + v-model="videoPlayDialogVisible" + title="瑙嗛棰勮" + width="60%" + @close="handleVideoPlayClose" + > + <div class="video-play-container"> + <video + ref="currentPlayVideo" + v-if="currentPlayUrl" + :src="currentPlayUrl" + controls + class="play-video" + ></video> + </div> + </el-dialog> <!-- 瀛楀箷鐢熸垚寮规 --> <el-dialog @@ -276,24 +291,11 @@ <!-- 鐗囧ご鐗囧熬璁剧疆寮规 --> <el-dialog v-model="headerFooterDialogVisible" - title="鐗囧ご鐗囧熬璁剧疆" + title="鐗囧ご鐗囧熬" width="70%" @close="pauseAllVideos('headerFooter')" > <el-tabs v-model="activeTab"> - <el-tab-pane label="璁剧疆鐗囧ご鐗囧熬" name="setting"> - <el-form :model="headerFooterForm" label-width="120px"> - <el-form-item label="鐗囧ご瑙嗛"> - <UploadFile v-model="headerFooterForm.titles" :fileType="['mp4']" :limit="1" @on-success="handleFileSuccess('audition', $event)"/> - </el-form-item> - <el-form-item label="鐗囧熬瑙嗛"> - <UploadFile v-model="headerFooterForm.trailer" :fileType="['mp4']" :limit="1" @on-success="handleFileSuccess1('audition', $event)"/> - </el-form-item> - <el-form-item> - <el-button type="primary" @click="applyHeaderFooter" :loading="applyingHeaderFooter">淇濆瓨璁剧疆</el-button> - </el-form-item> - </el-form> - </el-tab-pane> <el-tab-pane label="鍚堟垚瑙嗛" name="merge"> <el-form :model="formData1" label-width="120px"> <el-form-item label="瑙嗛鏍煎紡"> @@ -305,49 +307,81 @@ <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="鐗囧ご瑙嗛"> - <div class="video-container"> - <video - v-if="formData1.value.titles" - :src="formData1.value.titles" - controls - class="preview-video" - @error="handleVideoError('titles')" - ></video> - <div v-else class="no-video">鏆傛棤鐗囧ご瑙嗛</div> + <div class="video-select-container"> + <div class="video-grid"> + <div + v-for="item in titlesList" + :key="item.id" + class="video-card" + :class="{ 'is-selected': formData1.value.titles === item.url }" + @click="handleTitlesSelect(item)" + > + <div class="video-thumbnail"> + <video + :src="item.url" + class="thumbnail-video" + controls + ></video> + </div> + <div class="video-info"> + <span class="video-name">{{ item.name }}</span> + <el-icon v-if="formData1.value.titles === item.url" class="selected-icon"><Check /></el-icon> + </div> + </div> + </div> </div> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="鐗囧熬瑙嗛"> - <div class="video-container"> - <video - v-if="formData1.value.trailer" - :src="formData1.value.trailer" - controls - class="preview-video" - @error="handleVideoError('trailer')" - ></video> - <div v-else class="no-video">鏆傛棤鐗囧熬瑙嗛</div> + <div class="video-select-container"> + <div class="video-grid"> + <div + v-for="item in trailerList" + :key="item.id" + class="video-card" + :class="{ 'is-selected': formData1.value.trailer === item.url }" + @click="handleTrailerSelect(item)" + > + <div class="video-thumbnail"> + <video + :src="item.url" + class="thumbnail-video" + controls + ></video> + </div> + <div class="video-info"> + <span class="video-name">{{ item.name }}</span> + <el-icon v-if="formData1.value.trailer === item.url" class="selected-icon"><Check /></el-icon> + </div> + </div> + </div> </div> </el-form-item> </el-col> </el-row> <el-form-item> - <el-button - type="primary" - @click="hecheng" - :loading="applyingHeaderFooter" - :disabled="!formData1.value?.titles || !formData1.value?.trailer" - > - 寮�濮嬪悎鎴� - </el-button> - <span v-if="!formData1.value?.titles || !formData1.value?.trailer" class="ml-10px text-red-500"> - 璇峰厛璁剧疆鐗囧ご鍜岀墖灏捐棰� - </span> + <div style="display: flex; width: 100%;"> + <div style="flex: 1;"> + <span v-if="!formData1.value?.titles || !formData1.value?.trailer" class="text-red-500"> + 璇峰厛閫夋嫨鐗囧ご鍜岀墖灏捐棰� + </span> + </div> + <div style="margin-right: 165px;"> + <el-button + type="primary" + @click="hecheng" + :loading="applyingHeaderFooter" + :disabled="!formData1.value?.titles || !formData1.value?.trailer" + > + 寮�濮嬪悎鎴� + </el-button> + </div> + </div> </el-form-item> </el-form> </el-tab-pane> - <el-tab-pane label="棰勮涓庝笅杞�" name="preview"> + <el-tab-pane label="棰勮涓嬭浇" name="preview"> <el-row :gutter="20"> <el-col :span="12"> <div class="preview-section"> @@ -436,9 +470,9 @@ import axios from 'axios' import { config } from '@/config/axios/config' import {createVideo, createVideoMeger, videoMeger} from "@/api/pptTemplate"; -import { ArrowDown } from '@element-plus/icons-vue' +import { ArrowDown, Check, VideoPlay, VideoPause } from '@element-plus/icons-vue' import { ElMessageBox } from 'element-plus' - +import { listFile } from '@/api/system/file' const router = useRouter() const message = useMessage() const { t } = useI18n() @@ -492,7 +526,7 @@ // 鐗囧ご鐗囧熬寮规鐩稿叧 const headerFooterDialogVisible = ref(false) -const activeTab = ref('setting') +const activeTab = ref('merge') const headerFooterForm = reactive({ id: null as number | null, titles: '', @@ -500,16 +534,13 @@ }) const applyingHeaderFooter = ref(false) -const handleFileSuccess = (fileType,response) => { - if (fileType === 'audition') { - headerFooterForm.titles = response.data - } -}; -const handleFileSuccess1 = (fileType,response) => { - if (fileType === 'audition') { - headerFooterForm.trailer = response.data - } -}; +const titlesList = ref([]) +const trailerList = ref([]) + +// 瑙嗛鎾斁鐩稿叧 +const currentPlayUrl = ref('') +const videoRefs = ref<HTMLVideoElement[]>([]) + // 鑾峰彇瑙嗛鍒楄〃 const getList = async () => { loading.value = true @@ -1005,6 +1036,31 @@ polling.value = false } +// 鑾峰彇鐗囧ご鐗囧熬鍒楄〃 +const getTitlesTrailerList = async () => { + try { + // 鑾峰彇鐗囧ご鍒楄〃 + const titlesRes = await listFile({ type: 1 }) + titlesList.value = titlesRes.list || [] + + // 鑾峰彇鐗囧熬鍒楄〃 + const trailerRes = await listFile({ type: 2 }) + trailerList.value = trailerRes.list || [] + } catch (error) { + console.error('鑾峰彇鐗囧ご鐗囧熬鍒楄〃澶辫触:', error) + } +} + +// 澶勭悊鐗囧ご閫夋嫨 +const handleTitlesSelect = (item) => { + formData1.value.titles = item.url +} + +// 澶勭悊鐗囧熬閫夋嫨 +const handleTrailerSelect = (item) => { + formData1.value.trailer = item.url +} + // 澶勭悊鐗囧ご鐗囧熬鎸夐挳鐐瑰嚮 const handleHeaderFooter = async (row) => { headerFooterForm.id = row.id @@ -1013,7 +1069,8 @@ headerFooterForm.trailer = details.trailer || '' formData1.value = details headerFooterDialogVisible.value = true - activeTab.value = 'setting' + activeTab.value = 'merge' + await getTitlesTrailerList() } // 搴旂敤鐗囧ご鐗囧熬璁剧疆 @@ -1041,6 +1098,16 @@ const hecheng = async () => { try { applyingHeaderFooter.value = true + + // 1. 鍏堜繚瀛橀厤缃� + const saveParams = { + id: headerFooterForm.id, + titles: formData1.value.titles, + trailer: formData1.value.trailer + } + await pptTemplateApi.createVideo(saveParams) + + // 2. 鍐嶈皟鐢ㄥ悎鎴愭帴鍙� let obj = {} if (formData1.isvideo == '2') { obj = { @@ -1108,6 +1175,14 @@ onMounted(() => { getList() }) + +// 鎾斁棰勮 +const playPreview = (type: 'titles' | 'trailer') => { + const video = type === 'titles' ? formData1.value.titles : formData1.value.trailer + if (video) { + window.open(video, '_blank') + } +} </script> <style scoped> @@ -1188,6 +1263,7 @@ padding: 20px; border-radius: 4px; height: 100%; + min-height: 500px; } .preview-section h4 { @@ -1197,7 +1273,7 @@ .video-container { width: 100%; - height: 200px; + height: 400px; background: #000; border-radius: 4px; overflow: hidden; @@ -1223,61 +1299,109 @@ background: #f5f7fa; } -.button-group { - display: flex; - justify-content: center; - gap: 10px; -} - -.ml-10px { - margin-left: 10px; -} - -.text-red-500 { - color: #f56c6c; -} - -.subtitle-dialog :deep(.el-dialog__body) { - padding: 20px; - min-height: 500px; -} - -.preview-section { - background: #f5f7fa; - padding: 20px; - border-radius: 4px; - height: 100%; - min-height: 400px; -} - -.video-container { - width: 100%; - height: 300px; - background: #000; - border-radius: 4px; - overflow: hidden; - margin-bottom: 15px; - position: relative; -} - -.preview-video { - width: 100%; - height: 100%; - object-fit: contain; - background: #000; -} - -.no-video { - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - color: #909399; - font-size: 14px; - background: #f5f7fa; -} .video-container1{ - height: 490px !important; + height: 700px !important; +} + +.video-select-container { + display: flex; + flex-direction: column; + gap: 15px; +} + +.video-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); + gap: 20px; + padding: 16px; + background: #f5f7fa; + border-radius: 8px; + max-height: 400px; + overflow-y: auto; +} + +.video-card { + position: relative; + background: #fff; + border-radius: 8px; + overflow: hidden; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1); +} + +.video-card.is-selected { + box-shadow: 0 4px 16px 0 rgba(64, 158, 255, 0.2); +} + +.video-thumbnail { + position: relative; + width: 100%; + padding-top: 56.25%; /* 16:9 姣斾緥 */ + background: #000; + overflow: hidden; +} + +.thumbnail-video { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + object-fit: cover; +} + +.video-info { + padding: 12px; + display: flex; + align-items: center; + justify-content: space-between; + background: #fff; +} + +.video-name { + flex: 1; + font-size: 14px; + color: #303133; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin-right: 8px; +} + +.selected-icon { + color: var(--el-color-primary); + font-size: 18px; + flex-shrink: 0; +} + +/* 鑷畾涔夋粴鍔ㄦ潯鏍峰紡 */ +.video-grid::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +.video-grid::-webkit-scrollbar-thumb { + background: #c0c4cc; + border-radius: 3px; +} + +.video-grid::-webkit-scrollbar-track { + background: #f5f7fa; + border-radius: 3px; +} + +.video-play-container { + width: 100%; + background: #000; + border-radius: 4px; + overflow: hidden; + aspect-ratio: 16/9; +} + +.play-video { + width: 100%; + height: 100%; + object-fit: contain; } </style> -- Gitblit v1.9.3