From cebc981e4b3680566c046412db912d0270c1ca92 Mon Sep 17 00:00:00 2001
From: shenrongliang <1328040932@qq.com>
Date: 星期四, 17 四月 2025 14:09:53 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 easegen-front/src/views/chooseTemplate/index.vue |    3 
 easegen-front/src/views/myCourse/index.vue       |  814 ++++++++++++++++++++++++++++++++++++++-------------------
 easegen-front/src/config/axios/config.ts         |    2 
 3 files changed, 543 insertions(+), 276 deletions(-)

diff --git a/easegen-front/src/config/axios/config.ts b/easegen-front/src/config/axios/config.ts
index 8116508..6a88bfc 100644
--- a/easegen-front/src/config/axios/config.ts
+++ b/easegen-front/src/config/axios/config.ts
@@ -16,7 +16,7 @@
   /**
    * 鎺ュ彛璇锋眰瓒呮椂鏃堕棿
    */
-  request_timeout: 30000,
+  request_timeout: 120*1000,
 
   /**
    * 榛樿鎺ュ彛璇锋眰绫诲瀷
diff --git a/easegen-front/src/views/chooseTemplate/index.vue b/easegen-front/src/views/chooseTemplate/index.vue
index db17b7a..24c9298 100644
--- a/easegen-front/src/views/chooseTemplate/index.vue
+++ b/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
   })
diff --git a/easegen-front/src/views/myCourse/index.vue b/easegen-front/src/views/myCourse/index.vue
index 03ad81a..f203bcb 100644
--- a/easegen-front/src/views/myCourse/index.vue
+++ b/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">瑙嗛姝e湪鍚堟垚...</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="瀛楀箷鍐呭灏嗘樉绀哄湪杩欓噷锛圫RT鏍煎紡锛�"
+                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="瀛楀箷鍐呭灏嗘樉绀哄湪杩欓噷锛圫RT鏍煎紡锛�"
-            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
+  }
+}
+
+// 妫�鏌ヨ棰慤RL鏄惁鏈夋晥
+
+
+// 鑾峰彇瀹屾暣鐨勮棰慤RL
+
+
+// 澶勭悊瑙嗛鍔犺浇閿欒
+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>

--
Gitblit v1.9.3