Przeglądaj źródła

feat: add course batch tool generation feature and related i18n

1. add new i18n texts for zh-CN, zh-HK, en-US locales
2. add running state to main store for AI task status tracking
3. add pending task count display to thumbnail child page
4. add post-upload confirmation dialog for batch tool generation
5. add auto-trigger AI chat flow when batch task is confirmed
lsc 14 godzin temu
rodzic
commit
97496812ca

+ 7 - 1
src/components/CollapsibleToolbar/componets/aiChat.vue

@@ -1,4 +1,4 @@
-<template>
+<template>
   <div class="ai-chat-container">
     <div class="message-quick-box">
       <div class="message-quick-box-item" :class="{ 'active': isQuickActions.includes('quick') }">
@@ -438,6 +438,7 @@ const slidesStore = useSlidesStore()
 const mainStore = useMainStore()
 const gType = ref('chat')
 
+
 const sendAction = async (action: string) => {
   const md = new MarkdownIt()
   if (gType.value === 'chat' || gType.value === 'generate_interactive_web' || gType.value === 'trigger_tool_recommend' || gType.value === 'trigger_batch_tool') {
@@ -1026,6 +1027,11 @@ const addAiWeb = async (code: string) => {
   isEdit.value = false
 }
 
+defineExpose({
+  sendQuickAction,
+  sendMessage
+})
+
 onMounted(() => {
   session_name.value = uuidv4()
   getAgentModel(agentid1.value)

+ 18 - 5
src/components/CollapsibleToolbar/index2.vue

@@ -115,7 +115,7 @@
         </div>
       </div>
       <div class="submenu-content">
-        <AiChat :userid="props.userid" :userJson="userJson" :courseTitle="props.courseTitle" />
+        <AiChat ref="aiChatRef" :userid="props.userid" :userJson="userJson" :courseTitle="props.courseTitle" />
       </div>
     </div>
     <div class="submenu" :class="{ visible: activeSubmenu === 'uploadFile' }">
@@ -713,11 +713,11 @@
 </template>
 
 <script lang="ts" setup>
-import { ref, watch, computed } from 'vue'
+import { ref, watch, computed, useTemplateRef, nextTick } from 'vue'
 import { storeToRefs } from 'pinia'
 import useCreateElement from '@/hooks/useCreateElement'
 import useSlideHandler from '@/hooks/useSlideHandler'
-import { useSlidesStore } from '@/store'
+import { useSlidesStore, useMainStore } from '@/store'
 import { useSpeakingStore } from '@/store/speaking'
 import FileInput from '@/components/FileInput.vue'
 import AiChat from './componets/aiChat.vue'
@@ -776,8 +776,21 @@ const { currentSlide } = storeToRefs(slidesStore)
 
 const { createFrameElement } = useCreateElement()
 const { createSlide, createSlideByTemplate } = useSlideHandler()
-
-
+const mainStore = useMainStore()
+const { running } = storeToRefs(mainStore)
+const aiChatRef = useTemplateRef<HTMLElement>('aiChatRef')
+
+watch(() => running.value, (newValue) => {
+  if (newValue) {
+    // 使用 nextTick 确保 DOM 更新后再调用子组件方法
+    nextTick(() => {
+      toggleSubmenu('cocoai')
+      aiChatRef.value.sendQuickAction(lang.ssBatchInteractiveWeb)
+      aiChatRef.value.sendMessage()
+    })
+    mainStore.setRunningState(false)
+  }
+})
 
 import _ from 'lodash'
 

+ 17 - 0
src/components/CreateCourseDialog.vue

@@ -100,6 +100,8 @@ import useImport from '@/hooks/useImport'
 import FileInput from '@/components/FileInput.vue'
 import message from '@/utils/message'
 import { lang } from '@/main'
+import { showConfirmDialog } from '@/utils/confirmDialog'
+import { useMainStore } from '@/store'
 
 const emit = defineEmits<{
   (e: 'close'): void
@@ -108,6 +110,8 @@ const emit = defineEmits<{
 }>()
 
 const { importPPTXFile, exporting } = useImport()
+const mainStore = useMainStore()
+const { setRunningState } = mainStore
 const currentFileName = ref('')
 const parsingStatus = ref<'parsing' | 'success'>('parsing')
 const parsingAbortController = ref<AbortController | null>(null)
@@ -139,6 +143,19 @@ const handleFileUpload = async (files: FileList) => {
     // 调用importPPTXFile并传入signal
     await importPPTXFile(files, { signal, onclose: () => {
       emit('setTitle', file.name.replace(/\.[^/.]+$/, '')); emit('close')
+      showConfirmDialog({
+        title: lang.ssUploadSuccess,
+        content: lang.ssBatchInteractiveWebConfirm,
+        confirmText: lang.ssBatchInteractiveWebConfirmDesc,
+        cancelText: lang.ssBatchInteractiveWebConfirmDesc2,
+        width: 400,
+        onConfirm: () => {
+          setRunningState(true)
+        },
+        onCancel: () => {
+          console.log('取消删除')
+        },
+      })
     } })
   }
   catch (error) {

+ 5 - 0
src/store/main.ts

@@ -42,6 +42,7 @@ export interface MainState {
   childPageIndex: number
   userid: string
   courseTitle: string
+  running: boolean
 }
 
 const nanoid = customAlphabet('0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')
@@ -83,6 +84,7 @@ export const useMainStore = defineStore('main', {
     childPageIndex: 0, // 带选页当前页面索引
     userid: '', // 当前用户ID
     courseTitle: '', // 课程标题
+    running: false, // 是否正在运行AI任务
   }),
 
   getters: {
@@ -226,6 +228,9 @@ export const useMainStore = defineStore('main', {
     },
     setCourseTitle(courseTitle: string) {
       this.courseTitle = courseTitle
+    },
+    setRunningState(state: boolean) {
+      this.running = state
     }
   },
 })

+ 9 - 2
src/views/Editor/Thumbnails/ThumbnailChildPage.vue

@@ -8,10 +8,12 @@
       </svg>
     </div>
     <div class="child-page-label">{{ lang.ssChildPageLabel }}</div>
+    <div class="child-page-label">{{ lang.ssPendingCount }}({{ pendingCount }} / {{ props.toolsArray?.length || 0 }})</div>
   </div>
 </template>
 
 <script lang="ts" setup>
+import { computed } from 'vue'
 import { lang } from '@/main'
 import { useMainStore, useSlidesStore } from '@/store'
 import { storeToRefs } from 'pinia'
@@ -38,6 +40,11 @@ const handleClick = () => {
   changeSlideIndex(props.slideIndex || 0)
   setChildPageState(true, props.toolsArray || [], props.slideIndex || 0)
 }
+
+// 计算待处理任务数量
+const pendingCount = computed(() => {
+  return props.toolsArray?.filter(item => item.isSet === true).length || 0
+})
 </script>
 
 <style lang="scss" scoped>
@@ -47,8 +54,8 @@ const handleClick = () => {
   align-items: center;
   justify-content: center;
   padding: 5px;
-  width: 60px;
-  height: 60px;
+  width: 100px;
+  height: 75px;
   background-color: #fffbeb;
   border: 2px dashed #f59e0b;
   border-radius: 8px;

+ 6 - 1
src/views/lang/cn.json

@@ -939,5 +939,10 @@
   "ssCandidate": "候选",
   "ssKeep": "保留",
   "ssDiscard": "丢弃",
-  "ssKeepAll": "保留全部"
+  "ssKeepAll": "保留全部",
+  "ssUploadSuccess": "上传完成",
+  "ssBatchInteractiveWebConfirm": "是否需要为课件批量生成工具?",
+  "ssBatchInteractiveWebConfirmDesc": "是,立即添加",
+  "ssBatchInteractiveWebConfirmDesc2": "暂不需要",
+  "ssPendingCount": "待处理"
 }

+ 6 - 1
src/views/lang/en.json

@@ -939,5 +939,10 @@
   "ssCandidate": "Candidate",
   "ssKeep": "Keep",
   "ssDiscard": "Discard",
-  "ssKeepAll": "Keep All"
+  "ssKeepAll": "Keep All",
+  "ssUploadSuccess": "Upload Success",
+  "ssBatchInteractiveWebConfirm": "Need to generate tools for the course?",
+  "ssBatchInteractiveWebConfirmDesc": "Yes, add now",
+  "ssBatchInteractiveWebConfirmDesc2": "No",
+  "ssPendingCount": "Pending Count"
 }

+ 6 - 1
src/views/lang/hk.json

@@ -939,5 +939,10 @@
   "ssCandidate": "候選",
   "ssKeep": "保留",
   "ssDiscard": "丟棄",
-  "ssKeepAll": "保留全部"
+  "ssKeepAll": "保留全部",
+  "ssUploadSuccess": "上傳成功",
+  "ssBatchInteractiveWebConfirm": "是否需要為課件批量生成工具?",
+  "ssBatchInteractiveWebConfirmDesc": "是,立即添加",
+  "ssBatchInteractiveWebConfirmDesc2": "暂不需要添加",
+  "ssPendingCount": "待处理"
 }