Kaynağa Gözat

feat(speaking): AI 配置 section with per-mode prompt + greeting textareas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jimmylee 1 ay önce
ebeveyn
işleme
46a4bc6fd6

+ 117 - 1
src/views/Editor/EnglishSpeaking/configs/TopicDiscussionConfig.vue

@@ -213,6 +213,46 @@
         </div>
       </div>
 
+      <!-- AI 配置 -->
+      <div class="ai-config-section">
+        <button class="section-toggle" @click="aiConfigExpanded = !aiConfigExpanded">
+          <span class="section-toggle-label">{{ lang.ssAiConfig }}</span>
+          <svg
+            class="section-toggle-arrow"
+            :class="{ collapsed: !aiConfigExpanded }"
+            width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
+          >
+            <path d="M6 9l6 6 6-6" />
+          </svg>
+        </button>
+
+        <div v-if="aiConfigExpanded" class="ai-config-body">
+          <div class="form-row">
+            <label class="form-label">{{ lang.ssCustomGreeting }}</label>
+            <textarea
+              class="form-textarea"
+              rows="3"
+              :value="activeGreeting"
+              @input="onGreetingInput(($event.target as HTMLTextAreaElement).value)"
+            />
+            <div class="form-hint">{{ lang.ssCustomGreetingHint }}</div>
+          </div>
+
+          <div class="form-row">
+            <label class="form-label">{{ lang.ssCustomSystemPrompt }}</label>
+            <textarea
+              class="form-textarea"
+              rows="10"
+              :value="activeSystemPrompt"
+              @input="onSystemPromptInput(($event.target as HTMLTextAreaElement).value)"
+            />
+            <div v-if="store.config.videoScene.enabled" class="form-hint">
+              {{ lang.ssCustomSystemPromptPlaceholderHint }}
+            </div>
+          </div>
+        </div>
+      </div>
+
       <!-- 分割线 -->
       <div class="section-spacer"></div>
 
@@ -339,7 +379,7 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, nextTick } from 'vue'
+import { ref, computed, nextTick } from 'vue'
 import { storeToRefs } from 'pinia'
 import { lang } from '@/main'
 import { useSpeakingStore } from '@/store/speaking'
@@ -357,6 +397,35 @@ const store = useSpeakingStore()
 // UI 状态
 const learningGoalsExpanded = ref(true)
 const advancedExpanded = ref(false)
+const aiConfigExpanded = ref(true)
+
+// AI 配置 — 按当前模式路由到对应 store slot
+const activeGreeting = computed(() =>
+  store.config.videoScene.enabled
+    ? store.config.videoScene.greeting
+    : store.config.nonVideoScene.greeting
+)
+const activeSystemPrompt = computed(() =>
+  store.config.videoScene.enabled
+    ? store.config.videoScene.systemPrompt
+    : store.config.nonVideoScene.systemPrompt
+)
+
+function onGreetingInput(value: string) {
+  if (store.config.videoScene.enabled) {
+    store.config.videoScene.greeting = value
+  } else {
+    store.config.nonVideoScene.greeting = value
+  }
+}
+
+function onSystemPromptInput(value: string) {
+  if (store.config.videoScene.enabled) {
+    store.config.videoScene.systemPrompt = value
+  } else {
+    store.config.nonVideoScene.systemPrompt = value
+  }
+}
 
 // 词汇新增
 const isAddingVocab = ref(false)
@@ -1090,4 +1159,51 @@ const handleApply = async () => {
     background: #ea580c;
   }
 }
+
+// AI 配置区
+.ai-config-section {
+  padding: 12px 0;
+
+  .ai-config-body {
+    margin-top: 12px;
+    display: flex;
+    flex-direction: column;
+    gap: 16px;
+  }
+
+  .form-row {
+    display: flex;
+    flex-direction: column;
+    gap: 6px;
+  }
+
+  .form-label {
+    font-size: 13px;
+    font-weight: 500;
+    color: #374151;
+  }
+
+  .form-textarea {
+    width: 100%;
+    padding: 8px 12px;
+    border: 1px solid #d1d5db;
+    border-radius: 6px;
+    font-size: 13px;
+    font-family: inherit;
+    line-height: 1.5;
+    resize: vertical;
+    box-sizing: border-box;
+
+    &:focus {
+      outline: none;
+      border-color: #3b82f6;
+      box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.1);
+    }
+  }
+
+  .form-hint {
+    font-size: 12px;
+    color: #6b7280;
+  }
+}
 </style>