shenrongliang
2025-04-17 cebc981e4b3680566c046412db912d0270c1ca92
Merge remote-tracking branch 'origin/master'
已修改3个文件
819 ■■■■■ 文件已修改
easegen-front/src/config/axios/config.ts 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/src/views/chooseTemplate/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/src/views/myCourse/index.vue 814 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
easegen-front/src/config/axios/config.ts
@@ -16,7 +16,7 @@
  /**
   * 接口请求超时时间
   */
  request_timeout: 30000,
  request_timeout: 120*1000,
  /**
   * 默认接口请求类型
easegen-front/src/views/chooseTemplate/index.vue
@@ -190,8 +190,6 @@
                :src="selectPPT.pictureUrl"
                style="z-index: 1"
              />
              <!-- 画中画 -->
              <Vue3DraggableResizable
                v-if="selectPPT.innerPicture && selectPPT.innerPicture.src"
@@ -2063,6 +2061,7 @@
const chooseTemplate = (currTemplate) => {
  selectTemplate.value = cloneDeep(currTemplate)
  console.log('选择的模板信息:', currTemplate)
  console.log(templates.value)
  templates.value.forEach((item) => {
    item.isActive = false
  })
easegen-front/src/views/myCourse/index.vue
@@ -33,24 +33,37 @@
  <!-- 视频列表 -->
  <ContentWrap>
    <el-table v-loading="loading" :data="list" style="width: 100%">
      <el-table-column :label="t('myCourse.videoCode')" align="center" prop="id" width="100" />
      <el-table-column :label="t('myCourse.videoName')" align="center" prop="name" min-width="150" />
      <el-table-column :label="t('myCourse.videoName')" align="center" prop="name" min-width="180" />
      <el-table-column :label="t('myCourse.courseName')" align="center" prop="courseName" min-width="180">
        <template #default="scope">
          <el-link type="primary" @click="goDetail(scope.row.courseId)">{{ scope.row.courseName }}</el-link>
        </template>
      </el-table-column>
      <el-table-column :label="t('myCourse.status')" align="center" prop="status" width="120">
        <template #default="scope">
          <dict-tag v-if="scope.row.status==2 && scope.row.subtitlesAddStatus!=null" :type="DICT_TYPE.video_zi" :value="scope.row.subtitlesAddStatus" />
          <dict-tag v-else :type="DICT_TYPE.VIDEO_STATUS" :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column label="进度" align="center" prop="progressVideo" width="100">
        <template #default="scope">
          <span v-if="scope.row.status==2">100%</span>
          <span v-else>{{ Math.max(1, Math.min(calculateProgress(scope.row.progressVideo), 99)) }}%</span>
        </template>
      </el-table-column>
      <el-table-column label="排队个数" align="center" prop="pos" width="100">
        <template #default="scope">
          <span v-if="scope.row.pos==0">视频正在合成...</span>
          <span v-else>{{ scope.row.pos }}</span>
        </template>
      </el-table-column>
      <el-table-column label="进度" align="center" prop="progressVideo" width="100">
      <el-table-column :label="t('myCourse.errorReason')" align="center" prop="errorReason" width="150">
        <template #default="scope">
          <span v-if="scope.row.status==2">100%</span>
          <span v-else>{{ calculateProgress(scope.row.progressVideo) }}%</span>
        </template>
      </el-table-column>
      <el-table-column :label="t('myCourse.courseName')" align="center" prop="courseName" min-width="150">
        <template #default="scope">
          <el-link type="primary" @click="goDetail(scope.row.courseId)">{{ scope.row.courseName }}</el-link>
          <el-tooltip :content="scope.row.errorReason || '--'" placement="top">
            <span>
              {{ scope.row.errorReason ? (scope.row.errorReason.length > 10 ? scope.row.errorReason.slice(0, 10) + '...' : scope.row.errorReason) : '--' }}
            </span>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column
@@ -72,99 +85,49 @@
          {{ calculateDuration(scope.row.createTime, scope.row.finishTime) }}
        </template>
      </el-table-column>
      <el-table-column :label="t('myCourse.errorReason')" align="center" prop="errorReason" width="150">
        <template #default="scope">
          <el-tooltip :content="scope.row.errorReason || '--'" placement="top">
            <span>
              {{ scope.row.errorReason ? (scope.row.errorReason.length > 10 ? scope.row.errorReason.slice(0, 10) + '...' : scope.row.errorReason) : '--' }}
            </span>
          </el-tooltip>
        </template>
      </el-table-column>
      <el-table-column :label="t('myCourse.status')" align="center" prop="status" width="120">
        <template #default="scope">
          <dict-tag v-if="scope.row.status==2 && scope.row.subtitlesAddStatus!=null" :type="DICT_TYPE.video_zi" :value="scope.row.subtitlesAddStatus" />
          <dict-tag v-else :type="DICT_TYPE.VIDEO_STATUS" :value="scope.row.status" />
        </template>
      </el-table-column>
      <el-table-column :label="t('table.action')" align="center" width="230" fixed="right">
      <el-table-column :label="t('table.action')" align="center" width="280" fixed="right">
        <template #default="scope">
          <el-button-group>
            <template v-if="scope.row.status == 2">
              <el-button
                type="text"
                type="primary"
                link
                @click="openPreview(scope.row)"
                plain
              >
                {{t('myCourse.preview')}}
              </el-button>
              <el-button
                type="text"
                type="primary"
                link
                @click="handleDownload(scope.row.previewUrl, scope.row.courseName + '_视频')"
                v-if="scope.row.previewUrl!=null"
              >
                下载
              </el-button>
              <el-button
                type="primary"
                link
                @click="openSubtitleDialog(scope.row.id)"
                v-if="scope.row.status == 2"
              >
                字幕
              </el-button>
              <el-button
                type="primary"
                link
                @click="handleHeaderFooter(scope.row)"
                plain
              >
                片头片尾
              </el-button>
              <template v-if="scope.row.status == 2 || scope.row.status==3">
                <el-button
                  type="text"
                  @click="handleDelete(scope.row.id)"
                  plain
                >
                  {{ t('action.del') }}
                </el-button>
              </template>
              <template v-if="scope.row.status == 3">
                <el-button
                  type="text"
                  @click="reMegerMedia(scope.row.id)"
                  plain
                >
                  {{t('myCourse.resynthesize')}}
                </el-button>
              </template>
              <template v-if="scope.row.status == 2">
                <el-button
                  type="text"
                  @click="openSubtitleDialog(scope.row.id)"
                  plain
                >
                  字幕
                </el-button>
              </template>
            </template>
            <el-dropdown>
              <el-button type="text" plain>
                更多<el-icon class="el-icon--right"><arrow-down /></el-icon>
              <el-button
                type="danger"
                link
                @click="handleDelete(scope.row.id)"
                v-if="scope.row.status == 2 || scope.row.status==3 || scope.row.status==4"
              >
                {{ t('action.del') }}
              </el-button>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item
                    v-if="scope.row.subtitlesAddStatus == 2"
                    @click="handleDownload(scope.row.videoUrl, scope.row.courseName + '_字幕合成视频')"
                  >
                    <Icon class="mr-2" />下载字幕合成视频
                  </el-dropdown-item>
                  <el-dropdown-item
                    @click="handleDownload(scope.row.previewUrl, scope.row.courseName + '_视频')"
                  >
                    <Icon class="mr-2" />下载视频
                  </el-dropdown-item>
                  <el-dropdown-item
                    v-if="scope.row.compositeVideo!=null"
                    @click="handleDownload(scope.row.compositeVideo, scope.row.courseName + '_片头片尾视频')"
                  >
                    <Icon class="mr-2" />下载片头片尾视频
                  </el-dropdown-item>
                  <el-dropdown-item
                    v-if="scope.row.titles!=null && scope.row.trailer!=null"
                    @click="mergeHeaderFooter(scope.row.id)"
                  >
                    <Icon class="mr-2" />合成片头片尾视频
                  </el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
            </template>
          </el-button-group>
        </template>
      </el-table-column>
@@ -186,142 +149,260 @@
  <el-dialog
    v-model="subtitleDialogVisible"
    title="字幕查看修改"
    width="60%"
    width="70%"
    class="subtitle-dialog"
  >
    <el-form :model="subtitleForm" ref="subtitleFormRef">
      <el-row :gutter="20">
        <el-col :span="7">
          <el-form-item label="断句时间阈值" prop="timeThreshold" :rules="[
            { required: true, message: '请输入断句时间阈值', trigger: 'blur' },
            { pattern: /^\d+(\.\d+)?$/, message: '请输入有效数字', trigger: 'blur' }
    <el-tabs v-model="activeSubtitleTab">
      <el-tab-pane label="字幕设置" name="setting">
        <el-form :model="subtitleForm" ref="subtitleFormRef">
          <el-row :gutter="20">
            <el-col :span="7">
              <el-form-item label="断句时间阈值" prop="timeThreshold" :rules="[
                { required: true, message: '请输入断句时间阈值', trigger: 'blur' },
                { pattern: /^\d+(\.\d+)?$/, message: '请输入有效数字', trigger: 'blur' }
              ]">
                <el-input v-model="subtitleForm.timeThreshold" placeholder="例如:0.05" clearable />
              </el-form-item>
            </el-col>
            <el-col :span="7">
              <el-form-item label="语言" prop="language" :rules="[
                { required: true, message: '请选择语言', trigger: 'change' }
              ]">
                <el-select v-model="subtitleForm.language" placeholder="请选择语言" clearable>
                  <el-option label="中文" value="zh" />
                  <el-option label="英文" value="en" />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="10">
              <el-form-item>
                <el-button
                  type="primary"
                  @click="generateSubtitles"
                  :loading="subtitleForm.subtitlesStatus === 1"
                  :disabled="subtitleForm.subtitlesStatus === 1 || subtitleForm.subtitlesAddStatus === 1"
                >
                  {{ subtitleForm.subtitlesStatus === 1 ? '字幕生成中' : '生成字幕' }}
                </el-button>
                <el-button
                  type="primary"
                  @click="triggerFileUpload"
                  :disabled="subtitleForm.subtitlesStatus === 1 || subtitleForm.subtitlesAddStatus === 1"
                >
                  上传SRT文件
                  <input
                    ref="fileInput"
                    type="file"
                    accept=".srt"
                    style="display: none"
                    @change="handleFileUpload"
                  />
                </el-button>
              </el-form-item>
            </el-col>
          </el-row>
          <el-form-item label="字幕内容" prop="content" :rules="[
            { required: true, message: '请先生成或上传字幕内容', trigger: 'blur' }
          ]">
            <el-input v-model="subtitleForm.timeThreshold" placeholder="例如:0.05" clearable />
          </el-form-item>
        </el-col>
        <el-col :span="7">
          <el-form-item label="语言" prop="language" :rules="[
            { required: true, message: '请选择语言', trigger: 'change' }
          ]">
            <el-select v-model="subtitleForm.language" placeholder="请选择语言" clearable>
              <el-option label="中文" value="zh" />
              <el-option label="英文" value="en" />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="10">
          <el-form-item>
            <el-button
              type="primary"
              @click="generateSubtitles"
              :loading="generating || polling"
            >
              生成字幕
            </el-button>
            <el-button
              type="primary"
              @click="triggerFileUpload"
            >
              上传SRT文件
              <input
                ref="fileInput"
                type="file"
                accept=".srt"
                style="display: none"
                @change="handleFileUpload"
            <div style="width: 100%;" class="textarea-wrapper">
              <el-input
                class="scroll-outside"
                v-model="subtitleForm.content"
                type="textarea"
                :rows="20"
                placeholder="字幕内容将显示在这里(SRT格式)"
                resize="none"
                :disabled="!subtitleForm.content"
                @input="handleSubtitleChange"
              />
            </el-button>
            <el-button
              type="primary"
              @click="downloadSubtitles"
              :loading="generating || polling"
            >
              字幕视频合成
            </el-button>
              <div class="button-group">
                <el-button
                  type="primary"
                  @click="saveSubtitles"
                  :loading="saving"
                  :disabled="!subtitleForm.content || !isSubtitleModified"
                >
                  保存字幕
                </el-button>
                <el-button
                  type="primary"
                  @click="downloadSubtitles"
                  :loading="subtitleForm.subtitlesAddStatus === 1"
                  :disabled="!subtitleForm.content || subtitleForm.subtitlesStatus === 1"
                >
                  {{ subtitleForm.subtitlesAddStatus === 1 ? '字幕视频合成中' : '字幕视频合成' }}
                </el-button>
              </div>
            </div>
          </el-form-item>
        </el-col>
      </el-row>
      <el-form-item label="字幕内容" prop="content" :rules="[
        { required: true, message: '请先生成或上传字幕内容', trigger: 'blur' }
      ]">
        <div style="width: 100%;" class="textarea-wrapper">
          <el-input
            class="scroll-outside"
            v-model="subtitleForm.content"
            type="textarea"
            :rows="20"
            placeholder="字幕内容将显示在这里(SRT格式)"
            resize="none"
          />
          <el-button
            style="margin-top: 20px;float: right;margin-left: 20px"
            type="primary"
            :disabled="!subtitleForm.subtitlesUrl"
            @click="handleDownload(subtitleForm.subtitlesUrl,subtitleForm.courseName)"
          >
            {{t('myCourse.downloadSubtitles')}}
          </el-button>
          <el-button
            style="margin-top: 20px;float: right"
            type="primary"
            @click="saveSubtitles"
            :loading="saving"
            :disabled="!subtitleForm.content"
          >
            保存字幕
          </el-button>
        </div>
      </el-form-item>
      <el-form-item label="预览视频" v-if="subtitleForm.subtitlesAddStatus==2">
        <div style="width: 100%;">
          <video width="100%" :src="subtitleForm.videoUrl" controls></video>
          <el-button
            style="margin-top: 20px;float: right"
            type="primary"
            @click="handleDownload(subtitleForm.videoUrl,subtitleForm.videoUrl)"
          >
            下载视频
          </el-button>
        </div>
      </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="subtitleDialogVisible = false">关 闭</el-button>
      </div>
    </template>
        </el-form>
      </el-tab-pane>
      <el-tab-pane label="预览与下载" name="preview">
        <el-row :gutter="20">
          <el-col :span="24">
            <div class="preview-section">
              <h4>字幕视频</h4>
              <div class="video-container">
                <video
                  v-if="subtitleForm.videoUrl"
                  :src="subtitleForm.videoUrl"
                  controls
                  class="preview-video"
                ></video>
                <div v-else class="no-video">暂无字幕视频</div>
              </div>
              <div class="button-group">
                <el-button
                  type="primary"
                  @click="handleDownload(subtitleForm.videoUrl, subtitleForm.courseName + '_字幕视频')"
                  :disabled="!subtitleForm.videoUrl"
                >
                  下载字幕视频
                </el-button>
              </div>
            </div>
          </el-col>
        </el-row>
      </el-tab-pane>
    </el-tabs>
  </el-dialog>
  <!-- 片头片尾设置弹框 -->
  <el-dialog
    v-model="headerFooterDialogVisible"
    title="片头片尾设置"
    width="50%"
    width="70%"
  >
    <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>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="headerFooterDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="applyHeaderFooter" :loading="applyingHeaderFooter">应用</el-button>
      </div>
    </template>
    <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-select v-model="formData1.isvideo" class="!w-240px">
              <el-option :disabled="formData1.value?.subtitlesAddStatus!=2" label="字幕视频" :value="1" />
              <el-option label="原视频" :value="2" />
            </el-select>
          </el-form-item>
          <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>
              </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>
              </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>
          </el-form-item>
        </el-form>
      </el-tab-pane>
      <el-tab-pane label="预览与下载" name="preview">
        <el-row :gutter="20">
          <el-col :span="12">
            <div class="preview-section">
              <h4>视频</h4>
              <div class="video-container">
                <video
                  v-if="formData1.value?.videoUrl || formData1.value?.previewUrl"
                  :src="formData1.value?.videoUrl || formData1.value?.previewUrl"
                  controls
                  class="preview-video"
                ></video>
                <div v-else class="no-video">暂无视频</div>
              </div>
              <div class="button-group">
                <el-button
                  type="primary"
                  @click="handleDownload(formData1.value?.videoUrl || formData1.value?.previewUrl, formData1.value?.courseName + '_视频')"
                  :disabled="!formData1.value?.videoUrl && !formData1.value?.previewUrl"
                >
                  下载视频
                </el-button>
              </div>
            </div>
          </el-col>
          <el-col :span="12">
            <div class="preview-section">
              <h4>片头片尾视频</h4>
              <div class="video-container">
                <video
                  v-if="formData1.value?.compositeVideo"
                  :src="formData1.value.compositeVideo"
                  controls
                  class="preview-video"
                ></video>
                <div v-else class="no-video">暂无片头片尾视频</div>
              </div>
              <div class="button-group">
                <el-button
                  type="primary"
                  @click="handleDownload(formData1.value?.compositeVideo, formData1.value?.courseName + '_片头片尾视频')"
                  :disabled="!formData1.value?.compositeVideo"
                >
                  下载片头片尾视频
                </el-button>
              </div>
            </div>
          </el-col>
        </el-row>
      </el-tab-pane>
    </el-tabs>
  </el-dialog>
  <!-- 视频合成选择弹框 -->
  <el-dialog
    v-model="dialogVisible"
    title="视频合成中"
    title="视频合成"
    width="50%"
  >
    <el-form :model="formData1" label-width="120px">
      <el-form-item  label="视频格式">
      <el-form-item label="视频格式">
        <el-select v-model="formData1.isvideo">
          <el-option :disabled="formData1.videoUrl==null" label="字幕视频" :value="1" />
          <el-option :disabled="formData1.value.subtitlesAddStatus!=2" label="字幕视频" :value="1" />
          <el-option label="原视频" :value="2" />
        </el-select>
      </el-form-item>
@@ -347,6 +428,7 @@
import { config } from '@/config/axios/config'
import {createVideo, createVideoMeger, videoMeger} from "@/api/pptTemplate";
import { ArrowDown } from '@element-plus/icons-vue'
import { ElMessageBox } from 'element-plus'
const router = useRouter()
const message = useMessage()
@@ -377,6 +459,7 @@
const subtitleDialogVisible = ref(false)
const subtitleFormRef = ref()
const fileInput = ref<HTMLInputElement | null>(null)
const activeSubtitleTab = ref('setting')
const subtitleForm = reactive({
  videoId: null as number | null,
  timeThreshold: '0.05',
@@ -385,13 +468,17 @@
  subtitlesUrl: '',
  videoUrl: '',
  courseName: '',
  subtitlesAddStatus: null
  subtitlesAddStatus: null,
  originalContent: '',
  subtitlesStatus: null
})
const generating = ref(false)
const saving = ref(false)
const isSubtitleModified = ref(false)
// 片头片尾弹框相关
const headerFooterDialogVisible = ref(false)
const activeTab = ref('setting')
const headerFooterForm = reactive({
  id: null as number | null,
  titles: '',
@@ -527,14 +614,39 @@
  return `${hrs > 0 ? `${hrs}时` : ''}${mins > 0 ? `${mins}分` : ''}${secs}秒`
}
// 打开字幕弹框
// 处理字幕内容变化
const handleSubtitleChange = () => {
  isSubtitleModified.value = subtitleForm.content !== subtitleForm.originalContent
}
// 修改打开字幕弹窗的函数
const openSubtitleDialog = async (videoId: number) => {
  try {
    subtitleDialogVisible.value = true
    activeSubtitleTab.value = 'setting'
    subtitleForm.videoId = videoId
    // 重置表单状态
    subtitleForm.content = ''
    subtitleForm.originalContent = ''
    subtitleForm.videoUrl = ''
    subtitleForm.subtitlesUrl = ''
    subtitleForm.subtitlesStatus = null
    subtitleForm.subtitlesAddStatus = null
    isSubtitleModified.value = false
    generating.value = false
    polling.value = false
    const videoDetail = await pptTemplateApi.myCourseDetail(videoId)
    if (!videoDetail) {
      message.error('获取视频详情失败,请重试')
      subtitleDialogVisible.value = false
      return
    }
    subtitleForm.subtitlesAddStatus = videoDetail.subtitlesAddStatus
    subtitleForm.subtitlesStatus = videoDetail.subtitlesStatus
    subtitleForm.courseName = videoDetail.courseName
    if (videoDetail.subtitlesAddStatus === 2) {
@@ -545,7 +657,7 @@
      subtitleForm.videoUrl = ''
      generating.value = true
      polling.value = true
    }else {
    } else {
      subtitleForm.videoUrl = videoDetail.videoUrl || ''
      generating.value = false
      polling.value = false
@@ -561,28 +673,35 @@
          if (response.ok) {
            const srtContent = await response.text()
            subtitleForm.content = srtContent
            subtitleForm.originalContent = srtContent
          } else {
            subtitleForm.content = videoDetail.subtitlesContent || ''
            subtitleForm.originalContent = videoDetail.subtitlesContent || ''
          }
        } catch (error) {
          console.error('获取字幕内容失败:', error)
          subtitleForm.content = videoDetail.subtitlesContent || ''
          subtitleForm.originalContent = videoDetail.subtitlesContent || ''
        }
      } else if (videoDetail.subtitlesContent) {
        subtitleForm.content = videoDetail.subtitlesContent
        subtitleForm.originalContent = videoDetail.subtitlesContent
      }
    } else if (videoDetail.subtitlesStatus === 3) {
      generating.value = false
      polling.value = false
      subtitleForm.content = ''
      subtitleForm.originalContent = ''
    } else if (videoDetail.subtitlesStatus === 1) {
      generating.value = true
      polling.value = true
      subtitleForm.content = ''
    }else{
      subtitleForm.originalContent = ''
    } else {
      generating.value = false
      polling.value = false
    }
    isSubtitleModified.value = false
  } catch (error) {
    console.error('获取视频详情失败:', error)
    message.error('获取视频详情失败,请重试')
@@ -639,6 +758,7 @@
    }
    generating.value = true
    subtitleForm.subtitlesStatus = 1 // 设置字幕状态为生成中
    const params = {
      id: subtitleForm.videoId,
@@ -667,16 +787,20 @@
              if (response.ok) {
                const srtContent = await response.text()
                subtitleForm.content = srtContent
                subtitleForm.originalContent = srtContent
              }
            } catch (error) {
              console.error('Error fetching SRT file:', error)
            }
          } else if (videoDetail.subtitlesContent) {
            subtitleForm.content = videoDetail.subtitlesContent
            subtitleForm.originalContent = videoDetail.subtitlesContent
          }
          message.success(subtitleForm.courseName+' '+'字幕生成成功')
          subtitleForm.subtitlesStatus = 2 // 设置字幕状态为已完成
          stopPolling()
        } else if (videoDetail.subtitlesStatus === 3) {
          subtitleForm.subtitlesStatus = 3 // 设置字幕状态为失败
          stopPolling()
        } else if (attempts >= maxAttempts) {
          message.warning(subtitleForm.courseName+' '+'字幕生成超时,请稍后手动检查')
@@ -704,7 +828,7 @@
  }
}
// 保存字幕
// 修改保存字幕函数
const saveSubtitles = async () => {
  try {
    saving.value = true
@@ -740,10 +864,8 @@
    await pptTemplateApi.saveSubtitles(params)
    message.success('字幕保存成功')
    subtitleDialogVisible.value = false
    // 刷新列表
    getList()
    subtitleForm.originalContent = subtitleForm.content
    isSubtitleModified.value = false
  } catch (error) {
    console.error('保存字幕失败:', error)
    message.error(`保存字幕失败: ${error.message || '未知错误'}`)
@@ -770,7 +892,7 @@
  return srtContent
}
// 字幕视频合成
// 修改字幕视频合成函数
const downloadSubtitles = async () => {
  try {
    if (!subtitleForm.content.trim()) {
@@ -778,7 +900,23 @@
      return
    }
    generating.value = true
    if (isSubtitleModified.value) {
      try {
        await ElMessageBox.confirm(
          '修改的字幕没有保存,确定要生成视频吗?',
          '提示',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }
        )
      } catch {
        return
      }
    }
    subtitleForm.subtitlesAddStatus = 1 // 设置字幕视频合成状态为进行中
    const obj = {
      id: subtitleForm.videoId
    }
@@ -799,9 +937,7 @@
        if (videoDetail.subtitlesAddStatus === 2) {
          message.success(subtitleForm.courseName+' '+'字幕视频合成成功')
          if (videoDetail.previewUrl) {
            subtitleForm.content = ''
            stopPolling()
            subtitleDialogVisible.value = false
            getList()
          }
        } else if (videoDetail.subtitlesAddStatus === 3) {
@@ -829,8 +965,6 @@
    console.error('字幕视频合成失败:', error)
    message.error(`字幕视频合成失败: ${error.message || '未知错误'}`)
    stopPolling()
  } finally {
    generating.value = false
  }
}
@@ -844,25 +978,27 @@
}
// 处理片头片尾按钮点击
const handleHeaderFooter =async (row) => {
  console.log(row)
const handleHeaderFooter = async (row) => {
  headerFooterForm.id = row.id
  let details= await pptTemplateApi.myCourseDetail(row.id)
  console.log(details)
  let details = await pptTemplateApi.myCourseDetail(row.id)
  headerFooterForm.titles = details.titles || ''
  headerFooterForm.trailer = details.trailer || ''
  formData1.value = details
  headerFooterDialogVisible.value = true
  activeTab.value = 'setting'
}
// 应用片头片尾设置
const applyHeaderFooter = async () => {
  try {
    console.log('应用片头片尾设置:', headerFooterForm)
    const title = await pptTemplateApi.createVideo(headerFooterForm)
    console.log('创建视频标题:', title)
    if (title) {
      message.success('片头片尾设置成功')
      headerFooterDialogVisible.value = false
      getList()
      // 切换到合成视频标签页
      activeTab.value = 'merge'
      // 更新formData1的值
      const details = await pptTemplateApi.myCourseDetail(headerFooterForm.id!)
      formData1.value = details
    }
  } catch (error) {
    console.error('片头片尾设置出错:', error)
@@ -871,63 +1007,62 @@
    applyingHeaderFooter.value = false
  }
}
//合成片头片尾视频
const mergeHeaderFooter = async (id: number) => {
  try {
    let details= await pptTemplateApi.myCourseDetail(id)
    formData1.value=details
    dialogVisible.value = true
    console.log(formData1.value)
  }
  catch (error) {
    console.error(error)
  }
}
//合成片头片尾
// 合成片头片尾
const hecheng = async () => {
  try {
    console.log(formData1.value)
    applyingHeaderFooter.value = true
    let obj={}
    if (formData1.isvideo=='2'){
      obj={
        id:formData1.value.id,
        titles:formData1.value.titles,
        trailer:formData1.value.trailer,
        courseName:formData1.value.courseName,
        videoUrl:null,
        previewUrl:formData1.value.previewUrl
    let obj = {}
    if (formData1.isvideo == '2') {
      obj = {
        id: formData1.value.id,
        titles: formData1.value.titles,
        trailer: formData1.value.trailer,
        courseName: formData1.value.courseName,
        videoUrl: null,
        previewUrl: formData1.value.previewUrl
      }
      const res = await pptTemplateApi.createVideoMeger(obj)
      if (res) {
        message.success('视频合成成功')
        applyingHeaderFooter.value = true
        dialogVisible.value = false
        message.success('视频合成任务已提交,请稍后查看')
        getList()
      }
    }else if (formData1.isvideo=='1'){
      obj={
        id:formData1.value.id,
        titles:formData1.value.titles,
        trailer:formData1.value.trailer,
        courseName:formData1.value.courseName,
        videoUrl:formData1.value.courseName,
        previewUrl:null
    } else if (formData1.isvideo == '1') {
      obj = {
        id: formData1.value.id,
        titles: formData1.value.titles,
        trailer: formData1.value.trailer,
        courseName: formData1.value.courseName,
        videoUrl: formData1.value.videoUrl,
        previewUrl: null
      }
      const res = await pptTemplateApi.createVideoMeger(obj)
      if (res) {
        message.success('视频合成成功')
        applyingHeaderFooter.value = true
        dialogVisible.value = false
        message.success('视频合成任务已提交,请稍后查看')
        getList()
      }
    }
    // loading.value = true
    //
  }
  catch (error) {
  } catch (error) {
    console.error(error)
    message.error('视频合成失败')
  } finally {
    applyingHeaderFooter.value = false
  }
}
// 检查视频URL是否有效
// 获取完整的视频URL
// 处理视频加载错误
const handleVideoError = (type: 'titles' | 'trailer') => {
  console.error(`${type}视频加载失败`)
  if (type === 'titles') {
    formData1.value.titles = ''
  } else {
    formData1.value.trailer = ''
  }
}
@@ -945,7 +1080,7 @@
<style scoped>
.textarea-wrapper {
  position: relative;
  width: fit-content;
  width: 100%;
}
.scroll-outside {
@@ -976,4 +1111,137 @@
.el-dropdown {
  margin-left: 0;
}
.button-group {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 10px;
}
:deep(.el-dialog__body) {
  padding: 20px;
}
:deep(.el-form-item__label) {
  font-weight: 500;
}
:deep(.el-button--primary.is-link) {
  padding: 0 8px;
}
:deep(.el-tabs__nav) {
  margin-bottom: 20px;
}
:deep(.el-tabs__item) {
  font-size: 16px;
  padding: 0 20px;
}
:deep(.el-form-item) {
  margin-bottom: 22px;
}
:deep(.el-input.is-disabled .el-input__inner) {
  background-color: #f5f7fa;
  border-color: #e4e7ed;
  color: #606266;
}
.preview-section {
  background: #f5f7fa;
  padding: 20px;
  border-radius: 4px;
  height: 100%;
}
.preview-section h4 {
  margin: 0 0 15px 0;
  color: #606266;
}
.video-container {
  width: 100%;
  height: 200px;
  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;
}
.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;
}
</style>