lsc 2 months ago
parent
commit
e68e30301b

+ 67 - 11
src/views/Student/components/AIWorkModal.vue

@@ -34,6 +34,7 @@
 import { computed, watch, ref} from 'vue'
 import Modal from '@/components/Modal.vue'
 import MarkdownIt from 'markdown-it'
+import useImport from '@/hooks/useImport'
 // md.render(_addText)
 const props = defineProps<{
   visible: boolean
@@ -45,21 +46,62 @@ const emit = defineEmits<{
 }>()
 
 const md = new MarkdownIt()
+const { getFile } = useImport()
 
 const visible = computed({
   get: () => props.visible,
   set: (v: boolean) => emit('update:visible', v)
 })
 
-// const messageList = ref<any[]>([])
+// 存储解析后的内容
+const messageList = ref<any[]>([])
 
-const messageList = computed(() => {
-  let _result = []
+// 判断是否是 URL 链接
+const isUrl = (str: string): boolean => {
+  try {
+    const url = new URL(str)
+    return url.protocol === 'http:' || url.protocol === 'https:'
+  }
+  catch {
+    return false
+  }
+}
+
+// 从链接获取文件内容
+const loadContentFromUrl = async (url: string): Promise<string> => {
+  try {
+    const fileData = await getFile(url)
+    if (fileData && fileData.data) {
+      // 将 ArrayBuffer 转为字符串
+      const uint8Array = new Uint8Array(fileData.data)
+      const jsonStr = new TextDecoder('utf-8').decode(uint8Array)
+      return jsonStr
+    }
+    return ''
+  }
+  catch (error) {
+    console.error('获取文件内容失败:', error)
+    return ''
+  }
+}
 
-  if (props.work && props.visible) {
-    const messageListRaw = JSON.parse(props.work.content)
+// 处理内容
+const processContent = async (content: string) => {
+  let contentToParse = content
+  
+  // 如果是链接,先获取文件内容
+  if (isUrl(content)) {
+    contentToParse = await loadContentFromUrl(content)
+  }
+  
+  if (!contentToParse) {
+    return []
+  }
+  
+  try {
+    const messageListRaw = JSON.parse(contentToParse)
     messageListRaw.forEach((item:any) => {
-      if (item.messages.length) {
+      if (item.messages && item.messages.length) {
         item.messages.forEach((item2: any) => {
           // 如果已经包含html标签则不再渲染
           if (!/^(\s*<[^>]+>.*<\/[^>]+>\s*|<[^>]+\/>\s*)$/s.test(item2.content.trim())) {
@@ -68,13 +110,27 @@ const messageList = computed(() => {
         })
       }
     })
-    _result = messageListRaw
+    return messageListRaw
   }
+  catch (error) {
+    console.error('解析内容失败:', error)
+    return []
+  }
+}
 
-
-
-  return _result
-})
+// 监听 work 和 visible 变化
+watch(
+  () => [props.work, props.visible],
+  async () => {
+    if (props.work && props.visible) {
+      messageList.value = await processContent(props.work.content)
+    }
+    else {
+      messageList.value = []
+    }
+  },
+  { immediate: true }
+)
 
 // watch(() => props.visible, () => {
 //   if (props.work && props.visible) {

+ 92 - 22
src/views/Student/components/choiceQuestionDetailDialog.vue

@@ -71,7 +71,7 @@
         <div class="c_t15_content" v-show="!lookWorkData">
           <div
             class="c_t15_c_item"
-            v-for="item in workArray"
+            v-for="item in processedWorkArray"
             :key="item.id"
             @click="lookWork(item.id)"
           >
@@ -121,7 +121,7 @@
         <div class="c_t72_content" v-show="!lookWorkData">
           <div
             class="c_t72_c_item"
-            v-for="item in workArray"
+            v-for="item in processedWorkArray"
             :key="item.id"
             @click="lookWork(item.id)"
           >
@@ -199,7 +199,7 @@
         <div class="c_t73_content" v-show="!lookWorkData">
           <div
             class="c_t73_c_item"
-            v-for="item in workArray"
+            v-for="item in processedWorkArray"
             :key="item.id"
             @click="lookWork(item.id)"
           >
@@ -237,6 +237,7 @@ import { computed, ref, watch, onUnmounted, nextTick } from 'vue'
 import * as echarts from 'echarts'
 import previewImageTool from '../../components/tool/previewImageTool.vue'
 import MarkdownIt from 'markdown-it'
+import useImport from '@/hooks/useImport'
 const props = defineProps<{
   visible: number[];
   workIndex: number;
@@ -268,21 +269,59 @@ const workDetail = computed(() => {
 const previewImageToolRef = ref<any>(null)
 
 const md = new MarkdownIt()
+const { getFile } = useImport()
 
-const workArray = computed(() => {
-  let _result = []
-  if (props.workArray) {
-    const _workArray = JSON.parse(JSON.stringify(props.workArray))
-    if ([45, 15].includes(props.showData.toolType)) {
-      _workArray.forEach((i: any) => {
-        i.content = JSON.parse(decodeURIComponent(i.content))
-      })
+// 判断是否是 URL 链接
+const isUrl = (str: string): boolean => {
+  try {
+    const url = new URL(str)
+    return url.protocol === 'http:' || url.protocol === 'https:'
+  }
+  catch {
+    return false
+  }
+}
+
+// 从链接获取文件内容
+const loadContentFromUrl = async (url: string): Promise<string> => {
+  try {
+    const fileData = await getFile(url)
+    if (fileData && fileData.data) {
+      // 将 ArrayBuffer 转为字符串
+      const uint8Array = new Uint8Array(fileData.data)
+      const jsonStr = new TextDecoder('utf-8').decode(uint8Array)
+      return jsonStr
+    }
+    return ''
+  }
+  catch (error) {
+    console.error('获取文件内容失败:', error)
+    return ''
+  }
+}
+
+// 处理单个作业内容
+const processWorkContent = async (content: string, toolType: number): Promise<any> => {
+  let contentToParse = content
+  
+  // 如果是链接,先获取文件内容
+  if (isUrl(content)) {
+    contentToParse = await loadContentFromUrl(content)
+  }
+  
+  if (!contentToParse) {
+    return null
+  }
+  
+  try {
+    if ([45, 15].includes(toolType)) {
+      return JSON.parse(decodeURIComponent(contentToParse))
     }
-    if (props.showData.toolType === 72) {
-      _workArray.forEach((i: any) => {
-        i.content = JSON.parse(i.content)
-        i.content.forEach((item: any) => {
-          if (item.messages.length) {
+    else if (toolType === 72) {
+      const parsed = JSON.parse(contentToParse)
+      if (Array.isArray(parsed)) {
+        parsed.forEach((item: any) => {
+          if (item.messages && item.messages.length) {
             item.messages.forEach((item2: any) => {
               // 如果已经包含html标签则不再渲染
               if (
@@ -295,13 +334,44 @@ const workArray = computed(() => {
             })
           }
         })
-      })
+      }
+      return parsed
     }
-    _result = _workArray
+    return contentToParse
+  }
+  catch (error) {
+    console.error('解析内容失败:', error)
+    return null
   }
+}
 
-  return _result
-})
+const processedWorkArray = ref<any[]>([])
+
+// 监听 workArray 和 showData 变化
+watch(
+  () => [props.workArray, props.showData?.toolType],
+  async () => {
+    if (props.workArray && props.showData) {
+      const _workArray = JSON.parse(JSON.stringify(props.workArray))
+      
+      // 处理每个作业内容
+      for (const i of _workArray) {
+        if (i.content) {
+          const processedContent = await processWorkContent(i.content, props.showData.toolType)
+          if (processedContent !== null) {
+            i.content = processedContent
+          }
+        }
+      }
+      
+      processedWorkArray.value = _workArray
+    }
+    else {
+      processedWorkArray.value = []
+    }
+  },
+  { immediate: true, deep: true }
+)
 
 // 关闭对应的作业详细页面
 const closeSlideIndex = () => {
@@ -330,8 +400,8 @@ const lookWorkDetail = ref<string>('')
 const lookWorkData = computed(() => {
   let _result = null
 
-  if (lookWorkDetail.value && workArray.value.length > 0) {
-    const _workFind = workArray.value.find(
+  if (lookWorkDetail.value && processedWorkArray.value.length > 0) {
+    const _workFind = processedWorkArray.value.find(
       (i: any) => i.id === lookWorkDetail.value
     )
     if (_workFind) {

+ 26 - 13
src/views/Student/index.vue

@@ -1588,19 +1588,32 @@ const handleHomeworkSubmit = async () => {
         if (iframeWindow && iframeWindow.exposed_outputs) {
           console.log('执行iframe中的submitWork方法,参数可变')
           const iframeSlideIndex = slideIndex.value
-          const Cow = JSON.stringify(iframeWindow.exposed_outputs)
-          // 这里假设 submitWork 是全局可用的函数
-          await submitWork(iframeSlideIndex, '72', Cow, '20')
-          message.success('作业提交成功')
-          hasSubmitWork = true
+          const jsonString = JSON.stringify(iframeWindow.exposed_outputs)
           
-          // 发送作业提交成功的socket消息
-          sendMessage({
-            type: 'homework_submitted',
-            courseid: props.courseid,
-            slideIndex: slideIndex.value,
-            userid: props.userid
-          })
+          // 将 JSON 字符串转成文件并上传
+          try {
+            const blob = new Blob([jsonString], { type: 'application/json' })
+            const file = new File([blob], `ai_work_${Date.now()}.json`, { type: 'application/json' })
+            const fileUrl = await uploadFile(file)
+            console.log('文件上传成功,链接:', fileUrl)
+            
+            // 使用上传后的链接提交作业
+            await submitWork(iframeSlideIndex, '72', fileUrl, '20')
+            message.success('作业提交成功')
+            hasSubmitWork = true
+            
+            // 发送作业提交成功的socket消息
+            sendMessage({
+              type: 'homework_submitted',
+              courseid: props.courseid,
+              slideIndex: slideIndex.value,
+              userid: props.userid
+            })
+          }
+          catch (error) {
+            console.error('文件上传失败:', error)
+            message.error('作业提交失败,请重试')
+          }
         }
       }
       else if (slides.value[slideIndex.value].elements.some((element: any) => element.isHTML)) {
@@ -2262,7 +2275,7 @@ const selectCourseSLook = async (type = 2) => {
     }
     isFollowModeActive.value = true
     if (props.userid == courseDetail.value.userid && props.type == '1') {
-      await api.updateCourseFollowC(slideIndex.value, props.courseid as string)
+      api.updateCourseFollowC(slideIndex.value, props.courseid as string)
       sendMessage({slideIndex: slideIndex.value, courseid: props.courseid, type: 'slideIndex'})
       console.log('设置当前幻灯片为跟随目标:', slideIndex.value)
     }