Browse Source

增加处理诗词类型的脚本生成编辑

liyanbo 1 month ago
parent
commit
ed6b84b589

+ 22 - 2
src/views/bjdx/course/aiGenerate/VideoPreview.vue

@@ -27,9 +27,9 @@
               <strong>当前背景音:</strong> {{ currentSection.backgroundAudio.type }}
             </div>
             
-            <!-- 人物形象 - 数字人和提问对话显示 -->
+            <!-- 人物形象 - 数字人、提问和诗词对话显示 -->
             <div
-              v-if="currentDialogue && (currentDialogue.type === 'digital' || currentDialogue.type === 'quest')"
+              v-if="currentDialogue && (currentDialogue.type === 'digital' || currentDialogue.type === 'quest' || currentDialogue.type === 'poem')"
               :key="`character-${currentDialogue.roleName}-${currentDialogueIndex}`"
               class="character"
               :class="{
@@ -71,6 +71,22 @@
               <div class="dialogue-content" v-html="parseMarkdown(currentDialogue.content)"></div>
             </div>
 
+            <!-- 对话卡片 - 诗词对话 -->
+            <div
+              v-if="currentDialogue && currentDialogue.type === 'poem'"
+              :key="`dialogue-${currentDialogueIndex}`"
+              class="dialogue-card"
+              :class="{
+                'left': getCharacterSide(currentDialogue.roleName) === 'left',
+                'right': getCharacterSide(currentDialogue.roleName) === 'right'
+              }"
+            >
+              <div class="dialogue-header poem-header">
+                <span class="role-name">{{ getRoleName(currentDialogue.roleName) }}</span>
+              </div>
+              <div class="dialogue-content" v-html="parseMarkdown(currentDialogue.content)"></div>
+            </div>
+
             <!-- 用户回答状态 -->
             <div v-if="currentDialogue && currentDialogue.type === 'user'" class="user-input-section">
               <!-- 麦克风图标 -->
@@ -885,6 +901,10 @@ onUnmounted(() => {
   background: #E6A23C;
 }
 
+.dialogue-header.poem-header {
+  background: #909399;
+}
+
 /* 控制栏 */
 .video-controls {
   position: absolute;

+ 147 - 16
src/views/bjdx/course/aiGenerate/aiGengrate.vue

@@ -94,12 +94,25 @@
                     />
                   </el-select>
                 </div>
+
+                <div class="selection-item">
+                  <label>主题类型</label>
+                  <el-select
+                    v-model="selectedThemeType"
+                    size="large"
+                    placeholder="请选择主题类型"
+                    style="width: 300px"
+                  >
+                    <el-option label="课程通用" value=13 />
+                    <el-option label="诗词课" value=101 />
+                  </el-select>
+                </div>
               </div>
 
               <div class="button-group">
                 <button
                   class="generate-btn primary"
-                  :disabled="!scriptPrompt || !selectedMainTeacher || isGenerating"
+                  :disabled="!scriptPrompt || !selectedMainTeacher || !selectedThemeType || isGenerating"
                   @click="generateScript"
                 >
                   {{ isGenerating ? '生成中...' : '生成课程脚本' }}
@@ -232,12 +245,14 @@
                   >
                     <div class="dialogue-header">
                       <div class="dialogue-type-tag" :class="dialogue.type">
-                        {{
+                        {{ 
                           dialogue.type === 'digital'
                             ? '数字人'
                             : dialogue.type === 'user'
                               ? '用户'
-                              : '提问'
+                              : dialogue.type === 'quest'
+                                ? '提问'
+                                : '诗词'
                         }}
                       </div>
                     </div>
@@ -438,6 +453,86 @@
                           </div>
                         </div>
                       </template>
+
+                      <!-- 诗词 -->
+                      <template v-else-if="dialogue.type === 'poem'">
+                        <div class="dialogue-role-select">
+                          <el-select
+                            v-model="dialogue.roleName"
+                            placeholder="选择角色"
+                            style="width: 140px"
+                            clearable
+                          >
+                            <el-option
+                              v-for="role in digitalHumans"
+                              :key="role.id"
+                              :label="role.name"
+                              :value="role.name"
+                            />
+                          </el-select>
+                        </div>
+                        <div class="dialogue-content-container">
+                          <el-input
+                            v-model="dialogue.content"
+                            type="textarea"
+                            class="dialogue-content"
+                            placeholder="诗词内容..."
+                            :autosize="{ minRows: 2, maxRows: 4 }"
+                          />
+                        </div>
+                        <div class="action-buttons">
+                          <div class="action-buttons-row">
+                            <button
+                              v-if="dialogue.voiceoverUrl"
+                              class="play-btn"
+                              @click="playVoiceover(dialogue.voiceoverUrl)"
+                            >
+                              <span class="play-icon">{{
+                                audioState.isPlaying &&
+                                audioState.currentType === 'voice' &&
+                                audioState.currentUrl === dialogue.voiceoverUrl
+                                  ? '⏸'
+                                  : '▶'
+                              }}</span>
+                            </button>
+                            <button
+                              class="remove-btn"
+                              @click="removeDialogue(sectionIndex, dialogueIndex)"
+                              >×</button
+                            >
+                          </div>
+                          <div class="action-buttons-row">
+                            <button
+                              v-if="!dialogue.voiceoverUrl"
+                              class="generate-btn small"
+                              :disabled="
+                                !dialogue.content ||
+                                !dialogue.roleName ||
+                                dialogue.generatingVoiceover
+                              "
+                              @click="generateVoiceover(sectionIndex, dialogueIndex)"
+                            >
+                              <span class="voice-icon">{{
+                                dialogue.generatingVoiceover ? '生成中...' : '生成语音'
+                              }}</span>
+                            </button>
+                            <button
+                              v-if="dialogue.voiceoverUrl"
+                              class="generate-btn small"
+                              :disabled="
+                                !dialogue.content ||
+                                !dialogue.roleName ||
+                                dialogue.generatingVoiceover
+                              "
+                              @click="generateVoiceover(sectionIndex, dialogueIndex)"
+                            >
+                              <span class="voice-icon">{{
+                                dialogue.generatingVoiceover ? '生成中...' : '重新生成'
+                              }}</span>
+                            </button>
+                          </div>
+                        </div>
+                      </template>
                     </div>
                   </div>
 
@@ -451,6 +546,9 @@
                     <button class="add-dialogue-btn quest" @click="addQuestDialogue(sectionIndex)"
                       >+ 添加提问</button
                     >
+                    <button class="add-dialogue-btn poem" @click="addPoemDialogue(sectionIndex)"
+                      >+ 添加诗词</button
+                    >
                   </div>
                 </div>
               </div>
@@ -531,13 +629,15 @@
                         <div class="dialogue-header">
                           <div class="dialogue-header-left">
                             <div class="dialogue-type-tag" :class="dialogue.type">
-                              {{
-                                dialogue.type === 'digital'
-                                  ? '数字人'
-                                  : dialogue.type === 'user'
-                                    ? '用户'
-                                    : '提问'
-                              }}
+                              {{ 
+                                  dialogue.type === 'digital'
+                                    ? '数字人'
+                                    : dialogue.type === 'user'
+                                      ? '用户'
+                                      : dialogue.type === 'quest'
+                                        ? '提问'
+                                        : '诗词'
+                                }}
                             </div>
                             <div class="dialogue-role">
                               {{
@@ -628,6 +728,7 @@ const currentStep = ref(1)
 const scriptPrompt = ref('')
 const selectedMainTeacher = ref('')
 const selectedAssistants = ref([])
+const selectedThemeType = ref()
 const isGenerating = ref(false)
 
 // 对话框可见性
@@ -829,7 +930,7 @@ const audioInstances = new Map()
 const canProceed = computed(() => {
   switch (currentStep.value) {
     case 1:
-      return !!scriptPrompt.value && !!selectedMainTeacher.value
+      return !!scriptPrompt.value && !!selectedMainTeacher.value && !!selectedThemeType.value
     case 2:
     case 3:
       return scriptData.sections.every(
@@ -837,7 +938,7 @@ const canProceed = computed(() => {
           section.backgroundImage.url &&
           section.dialogues.every(
             (dialogue) =>
-              ((dialogue.type === 'digital' || dialogue.type === 'quest') &&
+              ((dialogue.type === 'digital' || dialogue.type === 'quest' || dialogue.type === 'poem') &&
                 dialogue.roleName &&
                 dialogue.content.trim() &&
                 dialogue.voiceoverUrl) ||
@@ -909,7 +1010,7 @@ const generateScript = async () => {
     })
     content += zhujiang.join(',') + ')'
 
-    await createAiRoleIdConversation(13)
+    await createAiRoleIdConversation(Number(selectedThemeType.value))
     currentStep.value = 2
     await doSendMessageStream(activeConversationId.value, content)
   } catch (error) {
@@ -1053,6 +1154,17 @@ const addQuestDialogue = (sectionIndex) => {
   })
 }
 
+// 步骤2:添加诗词
+const addPoemDialogue = (sectionIndex) => {
+  scriptData.sections[sectionIndex].dialogues.push({
+    type: 'poem',
+    content: '',
+    roleName: '',
+    voiceoverUrl: '',
+    generatingVoiceover: false
+  })
+}
+
 // 步骤2:删除对话
 const removeDialogue = (sectionIndex, dialogueIndex) => {
   scriptData.sections[sectionIndex].dialogues.splice(dialogueIndex, 1)
@@ -1234,8 +1346,8 @@ const playBackgroundAudio = (musicType) => {
 const generateVoiceover = async (sectionIndex, dialogueIndex) => {
   const dialogue = scriptData.sections[sectionIndex].dialogues[dialogueIndex]
 
-  // 只处理数字人和提问类型的对话
-  if (dialogue.type !== 'digital' && dialogue.type !== 'quest') {
+  // 只处理数字人、提问和诗词类型的对话
+  if (dialogue.type !== 'digital' && dialogue.type !== 'quest' && dialogue.type !== 'poem') {
     return
   }
 
@@ -1317,7 +1429,7 @@ const validateScript = () => {
     !scriptData.sections.every((s) =>
       s.dialogues.every(
         (d) =>
-          ((d.type === 'digital' || d.type === 'quest') &&
+          ((d.type === 'digital' || d.type === 'quest' || d.type === 'poem') &&
             d.roleName &&
             d.content.trim() &&
             d.voiceoverUrl) ||
@@ -1621,6 +1733,10 @@ onUnmounted(() => {
   background-color: #e6a23c;
 }
 
+.dialogue-type-tag.poem {
+  background-color: #909399;
+}
+
 /* 对话项样式 */
 .dialogue-item {
   position: relative;
@@ -1720,6 +1836,11 @@ onUnmounted(() => {
   color: white;
 }
 
+.add-dialogue-btn.poem {
+  background-color: #909399;
+  color: white;
+}
+
 .add-dialogue-btn.user:hover {
   background-color: #67c23a;
   border-color: #67c23a;
@@ -1732,6 +1853,12 @@ onUnmounted(() => {
   color: white;
 }
 
+.add-dialogue-btn.poem:hover {
+  background-color: #73767a;
+  border-color: #73767a;
+  color: white;
+}
+
 /* 预览对话样式 */
 .preview-dialogue {
   position: relative;
@@ -1753,6 +1880,10 @@ onUnmounted(() => {
   border-left: 4px solid #e6a23c;
 }
 
+.preview-dialogue.poem {
+  border-left: 4px solid #909399;
+}
+
 .preview-dialogue .dialogue-header {
   display: flex;
   align-items: center;