| | |
| | | </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 |
| | |
| | | <!-- ç头çå°¾è®¾ç½®å¼¹æ¡ --> |
| | | <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="è§é¢æ ¼å¼"> |
| | |
| | | <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"> |
| | |
| | | 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() |
| | |
| | | |
| | | // ç头ç尾弹æ¡ç¸å
³ |
| | | const headerFooterDialogVisible = ref(false) |
| | | const activeTab = ref('setting') |
| | | const activeTab = ref('merge') |
| | | const headerFooterForm = reactive({ |
| | | id: null as number | null, |
| | | titles: '', |
| | |
| | | }) |
| | | |
| | | 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 |
| | |
| | | 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 |
| | |
| | | headerFooterForm.trailer = details.trailer || '' |
| | | formData1.value = details |
| | | headerFooterDialogVisible.value = true |
| | | activeTab.value = 'setting' |
| | | activeTab.value = 'merge' |
| | | await getTitlesTrailerList() |
| | | } |
| | | |
| | | // åºç¨ç头ç尾设置 |
| | |
| | | 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 = { |
| | |
| | | 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> |
| | |
| | | padding: 20px; |
| | | border-radius: 4px; |
| | | height: 100%; |
| | | min-height: 500px; |
| | | } |
| | | |
| | | .preview-section h4 { |
| | |
| | | |
| | | .video-container { |
| | | width: 100%; |
| | | height: 200px; |
| | | height: 400px; |
| | | background: #000; |
| | | border-radius: 4px; |
| | | overflow: hidden; |
| | |
| | | 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> |