فهرست منبع

Merge branch 'beta' into HK

lsc 1 سال پیش
والد
کامیت
ef631e5555
53فایلهای تغییر یافته به همراه2820 افزوده شده و 748 حذف شده
  1. 4 0
      dist/index.html
  2. 1 0
      dist/static/css/app.582d2300aaf112f88f89d8a84b3e9274.css
  3. 0 0
      dist/static/css/app.582d2300aaf112f88f89d8a84b3e9274.css.map
  4. 1 0
      dist/static/css/app.b36acf10070bf14198fe7bf6466c90dc.css
  5. 1 0
      dist/static/css/app.b36acf10070bf14198fe7bf6466c90dc.css.map
  6. BIN
      dist/static/img/aiTeacherAvatar.802a64b.png
  7. BIN
      dist/static/img/top_pbl.ea9d02c.png
  8. BIN
      dist/static/img/userAvatar.6808cf3.png
  9. 1 0
      dist/static/js/app.b0ce7e44fcc1e0fe9ee4.js
  10. 0 0
      dist/static/js/app.b0ce7e44fcc1e0fe9ee4.js.map
  11. 1 0
      dist/static/js/app.ea5db34ba82ce44ab2e3.js
  12. 1 0
      dist/static/js/app.ea5db34ba82ce44ab2e3.js.map
  13. 0 0
      dist/static/js/manifest.6c4ea0d95d82bb3e7d94.js.map
  14. BIN
      src/assets/icon/pblCourse/aiTeacherAvatar.png
  15. BIN
      src/assets/icon/pblCourse/backIcon.png
  16. BIN
      src/assets/icon/pblCourse/bookIcon.png
  17. BIN
      src/assets/icon/pblCourse/changeIcon.png
  18. BIN
      src/assets/icon/pblCourse/copyIcon.png
  19. BIN
      src/assets/icon/pblCourse/doWorkIcon.png
  20. BIN
      src/assets/icon/pblCourse/fileIcon.png
  21. BIN
      src/assets/icon/pblCourse/phaseIcon.png
  22. BIN
      src/assets/icon/pblCourse/recordIcon.png
  23. BIN
      src/assets/icon/pblCourse/sendIcon.png
  24. BIN
      src/assets/icon/pblCourse/taskIcon.png
  25. BIN
      src/assets/icon/pblCourse/top_book.png
  26. BIN
      src/assets/icon/pblCourse/top_book_active.png
  27. BIN
      src/assets/icon/pblCourse/top_pbl.png
  28. BIN
      src/assets/icon/pblCourse/userAvatar.png
  29. 514 178
      src/components/pages/aiAddCourse/addCourse.vue
  30. 188 120
      src/components/pages/aiAddCourse/aiBox.vue
  31. 11 2
      src/components/pages/aiAddCourse/aiDialog.vue
  32. 79 42
      src/components/pages/classroomObservation/components/addNewCourseDialog.vue
  33. 9 5
      src/components/pages/classroomObservation/components/analysisItem.vue
  34. 21 3
      src/components/pages/classroomObservation/components/baseMessage.vue
  35. 52 66
      src/components/pages/classroomObservation/components/chatArea.vue
  36. 22 6
      src/components/pages/classroomObservation/components/messageArea.vue
  37. 0 281
      src/components/pages/classroomObservation/components/pdf.vue
  38. 73 19
      src/components/pages/classroomObservation/components/sharePdf.vue
  39. 2 2
      src/components/pages/classroomObservation/components/startPage.vue
  40. 6 15
      src/components/pages/classroomObservation/components/tape.vue
  41. 5 0
      src/components/pages/classroomObservation/components/transcription.vue
  42. 252 0
      src/components/pages/classroomObservation/components/vpdf.vue
  43. 5 1
      src/components/pages/classroomObservation/components/wangEnduit.vue
  44. 18 5
      src/components/pages/classroomObservation/index.vue
  45. 1 1
      src/components/pages/course.vue
  46. 538 0
      src/components/pages/pblCourse/component/chatArea.vue
  47. 185 0
      src/components/pages/pblCourse/component/doWorkArea.vue
  48. 158 0
      src/components/pages/pblCourse/component/procedureArea.vue
  49. 75 0
      src/components/pages/pblCourse/component/selectTopicDialog.vue
  50. 335 0
      src/components/pages/pblCourse/component/work.vue
  51. 252 0
      src/components/pages/pblCourse/index.vue
  52. 0 2
      src/components/pages/sz/teacher.vue
  53. 9 0
      src/router/index.js

+ 4 - 0
dist/index.html

@@ -32,7 +32,11 @@
       width: 100%;
       background: #e6eaf0;
       font-family: '黑体';
+<<<<<<< HEAD
     }</style><link href=./static/css/app.582d2300aaf112f88f89d8a84b3e9274.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.6c4ea0d95d82bb3e7d94.js></script><script type=text/javascript src=./static/js/vendor.72ddc9979219b8d2e842.js></script><script type=text/javascript src=./static/js/app.b0ce7e44fcc1e0fe9ee4.js></script></body></html><script>function stopSafari() {
+=======
+    }</style><link href=./static/css/app.b36acf10070bf14198fe7bf6466c90dc.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.571c38d63f24b1ae9e16.js></script><script type=text/javascript src=./static/js/vendor.3cd0a0187ca1f70ded67.js></script><script type=text/javascript src=./static/js/app.ea5db34ba82ce44ab2e3.js></script></body></html><script>function stopSafari() {
+>>>>>>> beta
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
dist/static/css/app.582d2300aaf112f88f89d8a84b3e9274.css


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
dist/static/css/app.582d2300aaf112f88f89d8a84b3e9274.css.map


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
dist/static/css/app.b36acf10070bf14198fe7bf6466c90dc.css


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
dist/static/css/app.b36acf10070bf14198fe7bf6466c90dc.css.map


BIN
dist/static/img/aiTeacherAvatar.802a64b.png


BIN
dist/static/img/top_pbl.ea9d02c.png


BIN
dist/static/img/userAvatar.6808cf3.png


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
dist/static/js/app.b0ce7e44fcc1e0fe9ee4.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
dist/static/js/app.b0ce7e44fcc1e0fe9ee4.js.map


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
dist/static/js/app.ea5db34ba82ce44ab2e3.js


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
dist/static/js/app.ea5db34ba82ce44ab2e3.js.map


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
dist/static/js/manifest.6c4ea0d95d82bb3e7d94.js.map


BIN
src/assets/icon/pblCourse/aiTeacherAvatar.png


BIN
src/assets/icon/pblCourse/backIcon.png


BIN
src/assets/icon/pblCourse/bookIcon.png


BIN
src/assets/icon/pblCourse/changeIcon.png


BIN
src/assets/icon/pblCourse/copyIcon.png


BIN
src/assets/icon/pblCourse/doWorkIcon.png


BIN
src/assets/icon/pblCourse/fileIcon.png


BIN
src/assets/icon/pblCourse/phaseIcon.png


BIN
src/assets/icon/pblCourse/recordIcon.png


BIN
src/assets/icon/pblCourse/sendIcon.png


BIN
src/assets/icon/pblCourse/taskIcon.png


BIN
src/assets/icon/pblCourse/top_book.png


BIN
src/assets/icon/pblCourse/top_book_active.png


BIN
src/assets/icon/pblCourse/top_pbl.png


BIN
src/assets/icon/pblCourse/userAvatar.png


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 514 - 178
src/components/pages/aiAddCourse/addCourse.vue


+ 188 - 120
src/components/pages/aiAddCourse/aiBox.vue

@@ -155,7 +155,7 @@ export default {
         { name: "任务设计" },
         { name: "评价设计" }
       ],
-      part: "",
+      part: "全部内容",
       checkBool: false,
       loading: false
     };
@@ -184,7 +184,192 @@ export default {
       }
     },
     addContent() {
+      let message = this.courseText
       if (this.courseText) {
+        let msg = `
+          ATTENTION: Use '##' to SPLIT SECTIONS, not '#'.Output format carefully referenced "Format example".`
+        if(this.checkArray.length){
+          let task = []
+          if(this.part == "全部内容"){
+            // msg += `--------
+            // ## 示例
+            // [
+            //   {
+            //     detail: "面向学生的任务描述",
+            //     elist:[
+            //       {"value":"评价名字1","detail":"评价维度1", "score": 5},
+            //       {"value":"评价名字2","detail":"评价维度2", "score": 5},
+            //       {"value":"评价名字3","detail":"评价维度3", "score": 5}
+            //     ],
+            //     toolChoose: [
+            //       {
+            //         tool:"电子白板", 
+            //         detail: "工具描述"
+            //       },
+            //       {
+            //         tool:"思维大图", 
+            //         detail: "工具描述"
+            //       }
+            //     ]
+            //   },
+            //   {
+            //     detail: "面向学生的任务描述",
+            //     elist:[
+            //       {"value":"评价名字1","detail":"评价维度1", "score": 5},
+            //       {"value":"评价名字2","detail":"评价维度2", "score": 5},
+            //       {"value":"评价名字3","detail":"评价维度3", "score": 5}
+            //     ],
+            //     toolChoose: []
+            //   },
+            //   {
+            //     detail: "面向学生的任务描述",
+            //     elist:[
+            //       {"value":"评价名字1","detail":"评价维度1", "score": 5},
+            //       {"value":"评价名字2","detail":"评价维度2", "score": 5},
+            //       {"value":"评价名字3","detail":"评价维度3", "score": 5}
+            //     ],
+            //     toolChoose: [
+            //       {
+            //         tool:"文档", 
+            //         detail: "工具描述"
+            //       }
+            //     ]
+            //   }
+            // ]
+            
+            // --------
+            // ## 输出格式与要求
+            // [
+            //   {
+            //     detail: "面向学生的任务描述",
+            //     elist:[
+            //       {"value":"评价名字1","detail":"评价维度1","score":5},
+            //       {"value":"评价名字2","detail":"评价维度2","score":5},
+            //       {"value":"评价名字3","detail":"评价维度3","score":5}
+            //     ],//至少3条评价标准,这个评价是教师用来评价学生表现的,需要包含评价维度,以及该维度中教师期待学生的表现,句式为学生应该能....
+            //     toolChoose: [
+            //       {
+            //         tool: "工具", //电子白板,文档,思维导图,表格,作业提交中选择其中一个工具
+            //         detail: "工具描述"
+            //       }
+            //     ] //可0~2个工具
+            //   }
+            // ]
+            // `
+            for(var i = 0; i < this.checkArray.length; i++){
+              let _index = this.checkArray[i]
+              task.push(this.course[_index])
+            }
+          }else if(this.part == "任务设计"){
+            // msg += `
+            // --------
+            // ## 示例
+            // [
+            //   {
+            //     detail: "面向学生的任务描述",
+            //     toolChoose: [
+            //       {
+            //         tool:"电子白板", 
+            //         detail: "工具描述"
+            //       },
+            //       {
+            //         tool:"思维大图", 
+            //         detail: "工具描述"
+            //       }
+            //     ]
+            //   },
+            //   {
+            //     detail: "面向学生的任务描述",
+            //     toolChoose: []
+            //   },
+            //   {
+            //     detail: "面向学生的任务描述",
+            //     toolChoose: [
+            //       {
+            //         tool:"文档", 
+            //         detail: "工具描述"
+            //       }
+            //     ]
+            //   }
+            // ]
+            
+            // --------
+            // ## 输出格式与要求
+            // [
+            //   {
+            //     detail: "面向学生的任务描述",
+            //     toolChoose: [
+            //       {
+            //         tool: "工具", //电子白板,文档,思维导图,表格,作业提交中选择其中一个工具
+            //         detail: "工具描述"
+            //       }
+            //     ] //可0~2个工具
+            //   }
+            // ]
+            // `
+            for(var i = 0; i < this.checkArray.length; i++){
+              let _index = this.checkArray[i]
+              task.push(this.course[_index])
+            }
+          }else if(this.part == "评价设计"){
+            // msg += `
+            // --------
+            // ## 示例
+            // [
+            //   {
+            //     elist:[
+            //       {"value":"评价名字1","detail":"评价维度1", "score": 5},
+            //       {"value":"评价名字2","detail":"评价维度2", "score": 5},
+            //       {"value":"评价名字3","detail":"评价维度3", "score": 5}
+            //     ],
+            //   },
+            //   {
+            //     elist:[
+            //       {"value":"评价名字1","detail":"评价维度1", "score": 5},
+            //       {"value":"评价名字2","detail":"评价维度2", "score": 5},
+            //       {"value":"评价名字3","detail":"评价维度3", "score": 5}
+            //     ],
+            //   },
+            //   {
+            //     elist:[
+            //       {"value":"评价名字1","detail":"评价维度1", "score": 5},
+            //       {"value":"评价名字2","detail":"评价维度2", "score": 5},
+            //       {"value":"评价名字3","detail":"评价维度3", "score": 5}
+            //     ],
+            //   }
+            // ]
+            
+            // --------
+            // ## 输出格式与要求
+            // [
+            //   {
+            //     elist:[
+            //       {"value":"评价名字1","detail":"评价维度1","score":5},
+            //       {"value":"评价名字2","detail":"评价维度2","score":5},
+            //       {"value":"评价名字3","detail":"评价维度3","score":5}
+            //     ],//至少3条评价标准,这个评价是教师用来评价学生表现的,需要包含评价维度,以及该维度中教师期待学生的表现,句式为学生应该能....
+            //   }
+            // ]
+            // `
+            for(var i = 0; i < this.checkArray.length; i++){
+              let _index = this.checkArray[i]
+              task.push(this.course[_index].eList)
+            }
+          }
+          
+          msg += `
+          --------
+          ## 修改内容
+          ${JSON.stringify(task)}
+          `
+
+          msg += `
+          --------
+          ## 要求
+          ${this.courseText}
+          `
+          message = msg
+        }
         let _uuid = uuidv4();
         this.array.push({
           role: "user",
@@ -209,7 +394,7 @@ export default {
           top_p: 1,
           frequency_penalty: 0,
           presence_penalty: 0,
-          messages: [{ role: "user", content: this.courseText }],
+          messages: [{ role: "user", content: message }],
           uid: _uuid,
           mind_map_question: ""
         });
@@ -228,123 +413,6 @@ export default {
           });
         this.getAiContent(_uuid);
       }
-      //             if (this.courseText) {
-
-      //                 this.loading = true
-      //                 this.array.push({ text: this.courseText, role: 2 })
-      //                 this.courseText = ''
-
-      //                 setTimeout(() => {
-      //                     this.loading = false
-      //                     if (this.courseText == '请告诉我任务四该具体如何实施?') {
-      //                         this.array.push({
-      //                             text: `任务四:设计保温杯的初步方案
-
-      // 教学目标:
-      // •学会应用热传递原理设计保温杯。
-      // •掌握基本的设计思维和创新方法。
-      // •理解材料选择对保温效果的影响。
-
-      // 教学过程:
-      // 1.复习:回顾热传递的基本原理和不同材料的导热性能。
-      // 2.需求分析:讨论保温杯的使用场景和功能需求,如保温时间、容量、便携性等。
-      // 3.设计指导:教师介绍设计原则和考虑因素,如材料的导热性能、结构设计、成本和环保等。
-      // 4.创意发散:学生团队进行头脑风暴,提出多种设计方案,并选出最可行的方案。
-      // 5.方案绘制:学生绘制保温杯的设计图,包括尺寸、形状、结构和所用材料。
-
-      // 师生研讨:
-      // •分享各组的设计方案,讨论设计的创新点和实用性。
-      // •教师提供专业意见和建议,帮助学生完善设计方案。
-      // •讨论如何将设计方案转化为实际操作的步骤。
-
-      // 拓展:
-      // •学生研究市场上不同类型保温杯的设计和功能。
-      // •分析保温杯的改进空间和潜在的创新点。
-
-      // 学生任务单:
-      // •列出保温杯的功能需求和设计目标。
-      // •绘制保温杯的设计方案图,并标注所用材料和尺寸。
-      // •简述所选材料的导热性能和对保温效果的影响。
-
-      // 知识点练习:
-      // 1.为什么保温杯通常使用不锈钢或陶瓷作为内胆材料?
-      // 2.描述一个创新的保温杯设计方案,并解释其工作原理。
-      // 3.讨论在设计保温杯时需要考虑的环境因素。
-
-      // 答案:
-      // 1.保温杯通常使用不锈钢或陶瓷作为内胆材料,因为这些材料是热的不良导体,可以有效减缓热量的散失。
-      // 2.一个创新的保温杯设计方案可能是采用多层结构,内层为不锈钢,中间层为真空隔热层,外层为塑料保护层。这种设计可以有效隔绝外界温度对热水温度的影响。
-      // 在设计保温杯时,需要考虑的环境因素包括材料的可回收性、生产过程中的能源消耗和废弃物处理等。`, role: 1
-      //                         })
-      //                     }else if(this.courseText == '请你重新设计该任务,我没有感温粉末。'){
-      //                         this.array.push({text:`任务一:探究热的传递方式
-
-      // 任务名: 观察热在水中的传递
-
-      // 任务描述:
-      // 同学们,今天我们将通过一个有趣的实验来探究热是如何在水中传递的。你将需要加热水并观察温度是如何分布的。请准备好实验器材,并按照安全指南进行操作。在实验过程中,请注意观察热水和冷水之间的相互作用,以及水温是如何随时间和空间变化的。你将需要记录你的观察结果,并思考热是如何从一个地方传递到另一个地方的。
-
-      // 工具名字: 电子白板
-
-      // 工具指引:
-      // 使用电子白板工具来绘制你的实验设置和观察到的热传递过程。你可以使用电子白板的绘图功能来创建一个温度分布图,展示热水和冷水相遇时的情况。同时,你可以用它来记录实验步骤和关键观察点,以便于你和同学们进行讨论和分享。
-
-      // 评价维度:
-      // •实验观察和记录:学生应该能够准确地记录实验过程中的观察结果,包括水温变化和热传递的现象。
-      // •数据分析和解释:学生应该能够分析实验数据,解释热在水中的传递方式,并能够用自己的话描述热对流的原理。
-      // •实验报告撰写:学生应该能够撰写一份清晰的实验报告,包括实验目的、方法、结果和结论,以及对实验过程的反思。`,role: 1})
-      //                             this.$emit('setUnitJson')
-      //                     }else if(this.checkArray.indexOf(0) !== -1 && this.courseText == '请告诉该具体如何实施?'){
-      //                         this.array.push({text:`教学活动1:探究热的传递方式
-      // 教学目标:
-      // •理解热的三种传递方式:传导、对流和辐射。
-      // •掌握热对流现象的基本概念。
-      // •学会通过实验观察和分析热在水中的传递过程。
-      // 教学过程:
-      // 1.引入:讨论日常生活中的热现象,如热水变凉、金属勺柄变热等,引出热传递的概念。
-      // 2.实验准备:介绍实验材料(试管、大烧杯、滴管、三脚架、石棉网、酒精灯、火柴、铁架台、试管架、感温粉末、红墨水、清水等)和安全注意事项。
-      // 3.实验操作:学生分组进行实验,加热试管和烧杯中的水,并加入感温粉末或红墨水以观察热的传递。
-      // 4.观察记录:学生记录实验现象,包括水温的变化、感温粉末或红墨水的颜色变化等。
-      // 5.结果分析:师生共同讨论实验结果,理解热对流的形成机制。
-      // 师生研讨:
-      // •讨论实验中观察到的热在水中的传递方式。
-      // •分析为什么水受热后会产生对流现象。
-      // •探讨热对流在生活中的应用,如暖气系统、热水器等。
-      // 拓展:
-      // •学生研究热在其他物质(如空气)中的传递方式。
-      // •设计并执行一个简单的实验,比较热在不同物质中的传递速度。
-      // 学生任务单:
-      // •描述实验中观察到的热在水中的传递过程。
-      // •解释热对流的形成原理。
-      // •提出一个生活中热对流的应用实例。
-      // 知识点练习:
-      // 1.热传递的三种方式是什么?
-      // 2.为什么加热试管底部的水会使试管上部的水也变热?
-      // 3.描述热对流的一个实际应用,并解释其工作原理。
-      // 答案:
-      // 1.热传递的三种方式是传导、对流和辐射。
-      // 2.加热试管底部的水会使试管上部的水也变热,因为热通过水的对流传递到了上部。
-      // 一个热对流的实际应用是暖气系统。它通过加热空气,使热空气上升,冷空气下降,形成对流循环,从而使房间变暖。`,role:1})
-      //                     }else if(this.checkArray.indexOf(0) !== -1 && this.courseText == '我没有感温粉末怎么办'){
-      //                         this.array.push({text:`如果您没有感温粉末,不用担心,我们仍然可以探究热的传递方式,只是需要采用不同的方法来观察和记录热在水中的传递。以下是几种替代方案:
-
-      // 1. **温度计**:您可以使用普通温度计或数字温度计来测量水在加热过程中的温度变化。通过在不同时间点记录水温,您可以绘制出温度随时间变化的曲线,从而理解热是如何在水中传播的。
-
-      // 2. **红墨水或其他有色液体**:如果家里有红墨水或其他任何有颜色的液体,您可以在水加热前滴入几滴。当红墨水在加热的水中扩散时,您可以观察到颜色的变化,这可以帮助您可视化水的流动和混合。
-
-      // 3. **视频记录**:您可以使用手机或相机录制水加热的过程,特别是观察加热区域与未加热区域之间的界面。通过回放视频,您可以分析和讨论热传递的现象。
-
-      // 4. **实验日志**:在实验过程中,详细记录您的观察和思考。包括您使用的替代方法、观察到的现象、遇到的问题以及您对这些现象的解释。
-
-      // 5. **讨论和交流**:与同学或老师讨论您的发现,即使没有感温粉末,您也可以通过交流想法和观察结果来增进对热传递现象的理解。
-
-      // 记住,科学探究的本质在于提出问题、设计实验、收集数据、分析结果和得出结论。即使缺少某些工具或材料,创新和适应性也是科学探究的重要部分。`,role:1})
-      //                     } else {
-      //                         this.array.push({ text: '请输入正确的文案', role: 1 })
-      //                     }
-      //                     // this.array.push({text: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', role: 1})
-      //                 }, 5000);
-      //             }
     },
     getAiContent(_uid) {
       let _source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/     https://gpt4.cocorobo.cn/stream/
@@ -414,7 +482,7 @@ export default {
     getChatList() {
       return new Promise((resolve, reject) => {
         if (this.loading) return this.$message.info("请稍等...");
-        this.chatList = [];
+        this.array = [];
         this.loading = true;
         let params = {
           userid: this.userid,

+ 11 - 2
src/components/pages/aiAddCourse/aiDialog.vue

@@ -19,6 +19,7 @@
 </template>
   
 <script>
+
 export default {
     props: {
         dialogVisibleAiD: {
@@ -27,6 +28,9 @@ export default {
         },
         aiText: {
             type: String,
+        },
+        clickType: {
+            type: Number
         }
     },
     data() {
@@ -37,8 +41,13 @@ export default {
     },
     watch:{
         dialogVisibleAiD(newValue, oldValue) {
-            this.text = JSON.parse(JSON.stringify(this.aiText))
-        }
+            if(newValue){
+                this.text = JSON.parse(JSON.stringify(this.aiText))
+                if(this.clickType == 2){
+                    this.confirm();
+                }
+            }
+        },
     },
     methods: {
         handleClose(done) {

+ 79 - 42
src/components/pages/classroomObservation/components/addNewCourseDialog.vue

@@ -1,24 +1,22 @@
 <template>
 	<div class="addNewCourse">
-		<el-dialog
-			title="添加课堂"
-			:visible.sync="dialogVisible"
-			width="500px"		>
-		<div>
-			<el-form ref="form" :model="form" label-position="top">
-			  <el-form-item label="课堂编号">
-  			  <el-input v-model="form.no" placeholder="请输入课堂编号"></el-input>
-  			</el-form-item>
-				<!-- <el-form-item label="课堂名称">
-  			  <el-input v-model="form.name" placeholder="请输入课堂名称"></el-input>
-  			</el-form-item> -->
-			</el-form>
-		</div>
+		<el-dialog title="添加课堂" :visible.sync="dialogVisible" width="500px">
+			<div>
+				<el-form ref="form" :model="form" :rules="rules" label-position="top">
+					<el-form-item label="课堂编号" prop="no">
+						<el-input v-model="form.no" placeholder="请输入课堂编号,支持英文和数数字结合"></el-input>
+					</el-form-item>
+					<el-form-item label="课堂名称" prop="name">
+						<el-input
+							v-model="form.name"
+							placeholder="请输入课堂名称"
+						></el-input>
+					</el-form-item>
+				</el-form>
+			</div>
 			<span slot="footer" class="dialog-footer">
 				<el-button @click="dialogVisible = false">取 消</el-button>
-				<el-button type="primary" @click="submit()"
-					>确 定</el-button
-				>
+				<el-button type="primary" @click="submit('form')">确 定</el-button>
 			</span>
 		</el-dialog>
 	</div>
@@ -26,38 +24,77 @@
 
 <script>
 export default {
-	emits:["success"],
-	data(){
-		return{
-			dialogVisible:false,
-			form:{
-				no:"",
-				name:"",
+	emits: ["success"],
+	props: {
+		courseList: {
+			type: Array,
+			default: () => [],
+		},
+	},
+	data() {
+		let validateNo = (rule, value, callback) => {
+			console.log(this.courseList.findIndex(i=>i.value==value))
+			if (value.trim().length == 0) {
+				callback(new Error("请输入课堂编号"));
+			}else if(this.courseList.findIndex(i=>i.value==value.trim())!=-1){
+				callback(new Error("该课堂编号已存在"));
+			} else {
+				callback();
+			}
+		};
+		let validateName = (rule, value, callback) => {
+			console.log(this.courseList)
+			console.log(rule)
+			if (value.trim().length == 0) {
+				callback(new Error("请输入课堂名称"));
+			} else {
+				callback();
+			}
+		};
+		return {
+			dialogVisible: false,
+			form: {
+				no: "",
+				name: "",
+			},
+			rules: {
+				no: [{ validator: validateNo,trigger: "change" }],
+				name: [{ validator: validateName, trigger: "change" }],
 			},
-		}
+		};
 	},
-	methods:{
-		open(){
+	methods: {
+		open() {
 			this.form = {
-				name:"",
-				no:"",
-			}
+				name: "",
+				no: "",
+			};
 			this.dialogVisible = true;
 		},
-		submit(){
-			if(this.form.no.trim().length==0 ){//|| this.form.name.trim().length==0
-				this.$message.error("请输入课堂编号")
-				return;
-			}
-			this.$emit("success",this.form)
-			this.dialogVisible = false;
-		}
-	}
+		submit(formName) {
+			this.$refs[formName].validate((valid) => {
+				if (valid) {
+					if (
+						this.form.no.trim().length == 0 ||
+						this.form.name.trim().length == 0
+					) {
+						//|| this.form.name.trim().length==0
+						this.$message.error("请填写完整信息");
+						return;
+					}
+					this.$emit("success", this.form);
+					this.dialogVisible = false;
+				} else {
+					console.log("error submit!!");
+					return false;
+				}
+			});
+		},
+	},
 };
 </script>
 
-<style  scoped>
-.addNewCourse{
-
+<style scoped>
+.addNewCourse {
 }
 </style>

+ 9 - 5
src/components/pages/classroomObservation/components/analysisItem.vue

@@ -1,6 +1,6 @@
 <template>
 	<div class="analysisItem">
-		<div class="ai-header">
+		<div class="ai-header" v-show="data.jsonData.name != '词频词汇分析'">
 			<div class="ai-h-left" @click.stop="changeOpenItem(!openItem)">
 				<span
 					:class="['ai-h-l-icon', openItem ? 'ai-h-l-iconActive' : '']"
@@ -44,9 +44,11 @@
 
 
 			<div class="a-m-brief">
-				<mdView :text="data.jsonData.result" />
+				{{ data.jsonData.result }}
+				<!-- <mdView :text="data.jsonData.result" /> -->
 				<!-- {{ data.jsonData.result }} -->
 			</div>
+			<mdView :text="data.jsonData.content" />
 			<div v-for="(item, index) in data.jsonData.dataFileList" :key="item.uid">
 				<div v-if="imgTypeList.includes(checkFileType(item.url))">
 					<img style="max-width: 100%" :src="item.url" alt="" />
@@ -184,7 +186,8 @@ export default {
 							return this.$message.error("AI无法识别优化");
 						}
 						let _copyData = JSON.parse(JSON.stringify(this.data));
-						_copyData.jsonData.result = _data.message;
+						// _copyData.jsonData.result = "";
+						_copyData.jsonData.content = _data.message;
 						_copyData.jsonData.dataFileList = [];
 						_copyData.jsonData.fileList = [];
 						_copyData.json_data = JSON.stringify(_copyData.jsonData);
@@ -229,7 +232,7 @@ export default {
 		}
 	},
 	mounted(){
-		if(this.data.jsonData.result){
+		if(this.data.jsonData.content){
 			this.historyResult.push(this.data.jsonData)
 		}
 	},
@@ -352,8 +355,9 @@ export default {
 .a-m-brief {
 	font-size: 16px;
 	/* 斜体 */
-	/* font-style: italic; */
+	font-style: italic;
 	margin-bottom: 10px;
+	color: #6b798e;
 }
 
 td,

+ 21 - 3
src/components/pages/classroomObservation/components/baseMessage.vue

@@ -156,8 +156,8 @@ export default {
 			from: {
 				courseName: "",
 				teacherName: "",
-				grade: "小学五年级",
-				subject: "科学",
+				grade: "",
+				subject: "",
 				class: "",
 				studentNum: 0,
 			},
@@ -169,7 +169,25 @@ export default {
 				{ value: "小学五年级", label: "小学五年级" },
 				{ value: "小学六年级", label: "小学六年级" },
 			],
-			subjectList: [{ value: "科学", label: "科学" }],
+			subjectList: [
+				{ value: "语文", label: "语文" },
+				{ value: "数学", label: "数学" },
+				{ value: "英语", label: "英语" },
+				{ value: "科学", label: "科学" },
+				{ value: "信息技术", label: "信息技术" },
+				{ value: "心理", label: "心理" },
+				{ value: "物理", label: "物理" },
+				{ value: "化学", label: "化学" },
+				{ value: "生物", label: "生物" },
+				{ value: "历史", label: "历史" },
+				{ value: "地理", label: "地理" },
+				{ value: "通用技术", label: "通用技术" },
+				{ value: "政治", label: "政治" },
+				{ value: "STEM", label: "STEM" },
+				{ value: "美术", label: "美术" },
+				{ value: "音乐", label: "音乐" },
+				{ value: "其他", label: "其他" },
+			],
 		};
 	},
 	methods: {

+ 52 - 66
src/components/pages/classroomObservation/components/chatArea.vue

@@ -33,14 +33,15 @@
 			<!-- 原文速递 -->
 			<transcription
 				v-show="pageStatus == 1 && !showIndexPage"
+				:showGetTextLoading="showGetTextLoading"
 				:data="transcriptionData"
 			>
-				<el-button
+				<!-- <el-button
 					style="position: absolute; bottom: 10px; right: 20px"
 					type="primary"
 					@click.stop="saveEditorBar(true)"
 					>保存</el-button
-				>
+				> -->
 			</transcription>
 			<!-- ai对话 -->
 			<tape
@@ -53,12 +54,12 @@
 			<!-- <div class="t-t-m-Item" v-show="cardStatus==1"> -->
 			<EditorBar
 				class="editorBar"
+				:showGetTextLoading="showGetTextLoading"
 				v-model="editorBarData.content"
-				v-show="
+				v-if="
 					pageStatus == 2 &&
 					!showIndexPage &&
-					editorBarData.type == '0' &&
-					!editorBarData.url
+					editorBarData.type == '0'
 				"
 				v-loading="uploadFileLoading"
 			>
@@ -71,8 +72,7 @@
 			</EditorBar>
 			<iframe
 				ref="viframe"
-				v-show="pageStatus == 2 && !showIndexPage && editorBarData.type == '1'"
-				v-if="editorBarData.url"
+				v-if="pageStatus == 2 && !showIndexPage && editorBarData.type == '1' && /\.(xlsx|doc|docx)$/i.test(editorBarData.url)"
 				style="width: 100%; height: 100%; border: none"
 				v-loading="uploadFileLoading"
 				:src="
@@ -80,6 +80,7 @@
 					encodeURIComponent(editorBarData.url)
 				"
 			></iframe>
+			<vpdf style="width: 100%; height: 100%;border: none" :pdfUrl="editorBarData.url" v-if="pageStatus == 2 && !showIndexPage && editorBarData.type == '1' && /\.(pdf)$/i.test(editorBarData.url)" />
 			<!-- </div> -->
 		</div>
 		<div class="ca-bottom">
@@ -91,7 +92,7 @@
 							<div class="ca-b-o-h-s-l-text">课堂观察</div>
 							<span class="ca-b-o-h-s-l-icon2 el-icon-caret-top"></span>
 						</div> -->
-
+<!-- 
 						<div class="ca-b-o-h-l-select" style="color: #3681fc">
 							<div
 								style="cursor: pointer"
@@ -115,16 +116,14 @@
 									>{{ i.lang }}</el-radio
 								>
 							</div>
-
-							<!-- <span class="ca-b-o-h-s-l-icon2 el-icon-caret-top"></span> -->
-						</div>
+						</div> -->
 
 						<div
 							class="ca-b-o-h-l-btn"
 							@click.stop="uploadRecording()"
 							v-loading="uploadFileLoading"
 						>
-							<div class="ca-b-o-h-b-l-text">上传录音</div>
+							<div class="ca-b-o-h-b-l-text">上传文件</div>
 						</div>
 					</div>
 					<div class="ca-b-o-h-right">
@@ -366,7 +365,7 @@ import Recorder from "js-audio-recorder";
 import MarkdownIt from "markdown-it";
 import EditorBar from "./wangEnduit.vue";
 const lamejs = require("lamejs");
-
+import vpdf from './vpdf'
 const recorder = new Recorder({
 	sampleBits: 16, // 采样位数,支持 8 或 16,默认是16
 	sampleRate: 48000, // 采样率,支持 11025、16000、22050、24000、44100、48000,根据浏览器默认值,我的chrome是48000
@@ -464,6 +463,7 @@ export default {
 		transcription,
 		tape,
 		EditorBar,
+		vpdf,
 	},
 	directives: {
 		"click-outside": clickOutside, // 注册自定义指令
@@ -484,6 +484,7 @@ export default {
 			textareaValue: "",
 			textareaLoading: false,
 			showRoleList: false,
+			showGetTextLoading:false,
 			roleListIndex: 0,
 			recordedForm: {
 				time: "00:00:00", //时间
@@ -535,52 +536,6 @@ export default {
 			],
 			transcriptionData: {
 				content: "",
-				// 				time: "2024-01-13 15:58:00",
-				// 				content: `对爱整洁,有精神、不拖拉,多思考。老师你好,同学们好。
-				// 请坐。前几天白老师关注到一则新闻,我们大鹏新区政府提出要将2024年作为旅游高质量发展年,为了吸引更多的游客来到咱们大鹏,你认为最重要的是什么来?第一小组。
-				// 我认为最重要的是要先把环境、绿水青山环境弄好,这样才能吸引更多的人来,大鹏是的。
-				// 大家同意他的想法吗?保护大棚环境人人有责,上周咱们班的两位环境调查员就去到葵冲进行了实地调查,我们来看看他们发现了什么。
-				// 大家好,我们是人大附中深圳学校五1班的张龙溪,何瑞一为了响应区政府的号召,今天我们作为环境调查员,准备实地调查一下葵冲的环境情况,走跟随我们的镜头一起去看一看我们调查到的情况有什么样,这样的。
-				// 应该是这样的,就是这样的,像这样的。
-				// 像这种情况。
-				// 像这种情况。
-				// 像这样的。
-				// 我们调查到的情况不是很好,同学们你们。
-				// 谁知谁知。
-				// 刚刚视频有一些小小的不同步,但不影响我们去观察里面的内容。
-				// 好,来,同学们,通过刚才的视频,你看到了哪些环境问题?第五小组。
-				// 我看到了菜市场上到处是垃圾,到处是那些烂掉的瓜果皮,是的。
-				// 其他小组呢?来第八小组我。
-				// 看我看到了路上还有很多漂移的垃圾就路上了。
-				// 你观察得很认真,是的,在咱们生活的这片区域确实存在很多的垃圾问题,这些垃圾主要是家庭里面产生的生活垃圾。
-				// 根据调查,一个家庭一天产生的垃圾大约是三千克,咱们班一共有45位同学,所有同学的家庭一天产生的生活垃圾总质量是多少?老师还调查到一些数据。我校小学部一共有1556人,整个大鹏新区共有55,224户家庭,整个深圳市有6,424,556户家庭,他们一天分别产生多少垃圾呢?现在请小组长从材料框当中拿出实验记录单,我们用计算器来速算一下,将结果记录下来。
-				// 好,可以开始计算。二十五六场这样做的之前工作。
-				// 还差一些,这样更准确一点。
-				// 用计算器这样更准确算完了没有?
-				// 你们。
-				// 算完了吗老师?
-				// 拍一下照片。
-				// 大家算完了吗?
-				// 好,算完的小组面向老师坐,好好表扬第八小组。好,第五、第四、第三。
-				// 好,这是第四小组的数据,我们来看跟大家的一样吗?一样,在刚刚大家记录数据的时候,你发现了什么?在你刚刚写下这些数据,有什么发现?第三小组。
-				// 我发现每天生产的垃圾会非常多。
-				// 垃圾很多多,到什么程度?我们来看。多到全班同学一天产生的垃圾,相当于一头成年公羊这么重,多到全校小学部家庭的垃圾,一天有一头非洲大象这么重。大鹏新区所有家庭一天产生的垃圾量相当于32头,非洲大象这么重,整个深圳市一天产生的垃圾高达3800头,非洲大象这么重。
-				// 老师看到很多同学发出来很惊讶的表情,整个深圳市就产生这么多的垃圾,整个广东省全国23个省加起来,放眼全球,全世界一年产生的垃圾是多少?我们来看这个统计图,把各个地区的垃圾量加起来,全球一年产生垃圾量高达20.17亿吨。这么多的垃圾如果填埋高度是一米的话,它可以埋掉三个深圳或者一个广州的面积。
-				// 面对这组数据,你有什么样的感受跟大家分享一下。来,第一小组,垃圾好多。
-				// 垃圾很多。
-				// 其他小组有什么样的感受?第八小组。
-				// 如果我们一直都以填埋的方式去处理垃圾的话,可能全球的土地都用于填埋垃圾了。
-				// 你还告诉了大家处理垃圾的一种方式是的,这么多的垃圾如果没有得到及时的处理,他们会侵占大量的土地包围我们的城市家园,我们就会陷入到一种垃圾围城的现状,面对这么庞大的垃圾量,我们该怎么办?
-				// 第七小组。
-				// 我觉得我们可以按照垃圾的种类来进行分类,比如说回收垃圾,它可以是回收,然后再利用的或者就是有害垃圾,比如说电池会污染,将会人们将会对它处以专业的处理。
-				// 你想到了垃圾分类,其他小组。
-				// 第五小组。
-				// 并且像很多我们要应该少用一次性的垃圾,比如说你买菜的时候要少用一次性的塑料袋,尽量用布袋,这样子的话可以节省既节省资源,又节省了垃圾的。
-				// 生产量,你说的是一个非常实用的方法,大家刚刚都说出来了,实际的行动是的。今天就让我们一起行动起来,一起共同想办法来解决垃圾问题。
-				// 面对垃圾问题。
-				// 正如第八小组刚刚说的,咱们国家早期的处理方式是直接填埋和焚烧发电,这两种方法在一定程度上减少了垃圾,但也存在着很严重的弊端。现在我们就一起来说一说这两种方法的好处和弊端。第一小组。
-				// 直接填埋这种方法会占用大量土地,而且还会污染土地,焚烧发电这种方法会会污_
-				// `,
 			},
 			chatList: [],
 		};
@@ -621,6 +576,10 @@ export default {
 			input.onchange = () => {
 				this.uploadFileLoading = true;
 				let file = input.files[0];
+				if(!/\.(wav|txt|pdf|xlsx|doc|docx)$/i.test(file.name)){
+					this.uploadFileLoading = false;
+					return this.$message.info("请上传.wav,.txt,.pdf,.xlsx,.doc,.docx格式的文件")
+				}
 				this.uploadFile(file);
 				// this.uploadWavFileAndGetText(file);
 			};
@@ -632,13 +591,21 @@ export default {
 				this.controlsStatus = 3;
 			} else if (this.pageStatus == 1 || this.pageStatus == 2) {
 				// 原文速览&&转录文稿
-				this.controlsStatus = 2;
+				
+				if([1,2].includes(this.recordedForm.status)){
+					this.controlsStatus = 1;
+				}else{
+					this.controlsStatus = 2;
+				}
 			}
 			this.showIndexPage = false;
 		},
 		cutTape(val) {
 			if (val == 0) {
-				if (this.audioUrl) {
+				if([1,2].includes(this.recordedForm.status)){
+					this.controlsStatus = 1;
+					this.showIndexPage = true;
+				}else if (this.audioUrl) {
 					this.controlsStatus = 2;
 					this.pageStatus = 1;
 				} else {
@@ -742,6 +709,7 @@ export default {
 			this.showIndexPage = false;
 			this.pageStatus = 1;
 			this.controlsStatus = 2;
+			this.recordedForm.status = 3
 			// let file = this.convertToMp3(recorder.getWAV());
 			const mp3Blob = recorder.getWAVBlob();
 			let audioFile = this.dataURLtoAudio(mp3Blob, "wav");
@@ -1053,16 +1021,23 @@ export default {
 		},
 		wavFileGetText(audioFile) {
 			let flag = true;
-			// if(flag){
+			// if (flag) {
 			// 	// 这里上传文件
-			// 	this.uploadWavFile(audioFile)
+			// 	// _this.uploadWavFile(audioFile);
 			// 	this.controlsStatus = 2;
 			// 	this.showIndexPage = false;
 			// 	this.pageStatus = 1;
+			// 	this.editorBarData.type = "0";
 			// 	flag = false;
+			// 	this.uploadFileLoading = false;
 			// }
-			// this.transcriptionData.content += "这是个测试";
-			// this.editorBarData.content +="这是个测试";
+			// this.showGetTextLoading = true;
+			// this.transcriptionData.content += "这是测试";
+			// this.editorBarData.content += "这是测试";
+
+			// setTimeout(()=>{
+			// 	this.showGetTextLoading = false;
+			// },4000)
 			// return;
 			let iiframe = this.$refs["iiframe"];
 			let _this = this;
@@ -1081,10 +1056,22 @@ export default {
 					flag = false;
 					_this.uploadFileLoading = false;
 				}
+				_this.showGetTextLoading = true;
 				let privText = e.privText;
+				console.log(privText)
 				_this.transcriptionData.content += privText;
 				_this.editorBarData.content += privText;
 			};
+
+			
+			iiframe.contentWindow.onSessionStopped = function(e){
+				console.log('转译完成')
+				console.log(e)
+				_this.$message.success("转译完成")
+				_this.showGetTextLoading = false;
+				_this.saveEditorBar();
+			}
+
 			iiframe.contentWindow.doContinuousPronunciationAssessment("", {
 				files: [audioFile],
 			});
@@ -1318,7 +1305,6 @@ export default {
 				blob.name = `${this.tid}-classroomObservation.txt`;
 				this.uploadFile(blob);
 			} else {
-				if(!this.editorBarData.content)return this.$message.info("请输入内容")
 				this.$emit("updateTranscription", {
 					transcriptionData: this.transcriptionData.content,
 					editorBarData: this.editorBarData,

+ 22 - 6
src/components/pages/classroomObservation/components/messageArea.vue

@@ -66,7 +66,7 @@
 				:fileId="fileId"
 				v-loading="extendLoading"
 			/>
-			<analysis
+			<!-- <analysis
 				@updateMessage="updateMessage"
 				ref="analysis3"
 				@delItem="delAnalysisItem"
@@ -78,7 +78,7 @@
 				:tid="tid"
 				:fileId="fileId"
 				v-loading="valueAddedLoading"
-			/>
+			/> -->
 			<!-- <currencyAnalysis
 				@updateMessage="updateMessage"
 				@delItem="delAnalysisItem"
@@ -175,7 +175,7 @@
 							</div>
 							<div class="a-d-b-i-bottom">{{ item.brief }}</div>
 							<div class="a-d-b-i-bottomPer" style="display: block">
-								3982人已使用
+								{{item.sum}}人已使用
 							</div>
 							<div class="a-d-b-i-bottomBtn" style="display: none">
 								<div
@@ -245,7 +245,7 @@ export default {
 				{ id: 0, name: "通用课堂分析" },
 				{ id: 1, name: "科学课堂分析" },
 				{ id: 2, name: "扩展分析" },
-				{ id: 3, name: "增值性分析" },
+				//{ id: 3, name: "增值性分析" },
 			],
 			dialogTagDataList: [
 				{
@@ -253,30 +253,35 @@ export default {
 					brief: "多维度分析课堂整体情况",
 					value: "4cc367c1-0076-11ef-aaca-12e77c4cb76b",
 					type: 0,
+					sum:157,
 				},
 				{
 					title: "教学阶段九事件分析",
 					brief: "使用加涅九事件分析教学环节",
 					value: "5e0466b3-0075-11ef-aaca-12e77c4cb76b",
 					type: 0,
+					sum:540
 				},
 				{
 					title: "布鲁姆问题分类",
 					brief: "多维度分析课堂整体情况",
 					value: "eac63117-00a7-11ef-aaca-12e77c4cb76b",
 					type: 0,
+					sum:405
 				},
 				{
 					title: "麦卡锡问题分类",
 					brief: "多维度分析课堂整体情况",
 					value: "18545cf7-0125-11ef-aaca-12e77c4cb76b",
 					type: 0,
+					sum:360
 				},
 				{
 					title: "学生回答情况",
 					brief: "多维度分析课堂整体情况",
 					value: "d3f75199-eb4e-11ee-aaca-12e77c4cb76b",
 					type: 0,
+					sum:206
 				},
 				// {
 				// 	title: "课堂时间分配",
@@ -298,36 +303,42 @@ export default {
 					brief: "多维度分析课堂整体情况",
 					value: "41d2d2d4-0125-11ef-aaca-12e77c4cb76b",
 					type: 1,
+					sum:430
 				},
 				{
 					title: "学科核心素养发展",
 					brief: "学科核心素养发展",
 					value: "b13a98de-0125-11ef-aaca-12e77c4cb76b",
 					type: 1,
+					sum:234
 				},
 				{
 					title: "科学教育目标分析",
 					brief: "科学教育目标分析",
 					value: "b13a98de-0125-11ef-aaca-12e77c4cb76b",
 					type: 1,
+					sum:513
 				},
 				{
 					title: "PORTAAL课堂观察",
 					brief: "PORTAAL课堂观察",
 					value: "8ab07d41-e143-11ee-aaca-12e77c4cb76b",
 					type: 1,
+					sum:173
 				},
 				{
 					title: "UTOP课堂观察",
 					brief: "UTOP课堂观察",
 					value: "8e3a389b-014f-11ef-aaca-12e77c4cb76b",
 					type: 1,
+					sum:329
 				},
 				{
 					title:"L-PST模型",
 					brief: "L-PST模型",
 					value: "e649112e-0150-11ef-aaca-12e77c4cb76b",
 					type: 1,
+					sum:421
 				},
 				// {
 				// 	title: "RST模型",
@@ -336,34 +347,39 @@ export default {
 				// 	type: 1,
 				// },
 				{
-					title: "RTOP模型",
+					title: "RTOP课堂观察",
 					brief: "多维度分析课堂整体情况",
 					value: "68265b18-0151-11ef-aaca-12e77c4cb76b",
 					type: 1,
+					sum:527
 				},
 				{
 					title: "课程质量评价",
 					brief: "多维度分析课堂整体情况", 
 				 	value:"25e53379-0152-11ef-aaca-12e77c4cb76b",
 					type: 1,
+					sum:391
 				},
 				{
 					title: "SCOP课堂观察",
 					brief: "多维度分析课堂整体情况",
 					value: "d0c76d35-0152-11ef-aaca-12e77c4cb76b",
 					type: 1,
+					sum:407
 				},
 				{
 					title: "5E课程改编",
 					brief: "5E课程改编",
 					value: "f757826e-0125-11ef-aaca-12e77c4cb76b",
 					type: 2,
+					sum:510
 				},
 				{
 					title: "5EX课程改编",
 					brief: "5EX课程改编",
 					value: "0b6b08b7-0126-11ef-aaca-12e77c4cb76b",
 					type: 2,
+					sum:611
 				},
 
 			],
@@ -660,7 +676,7 @@ export default {
 			this.getCurrencyAndBaseMessageData();
 			this.getScienceData();
 			this.getExtendData();
-			this.getValueAddedData();
+			// this.getValueAddedData();
 			// this.getFileId();
 		},
 		saveData(data) {

+ 0 - 281
src/components/pages/classroomObservation/components/pdf.vue

@@ -1,281 +0,0 @@
-<template>
-  <div class="pdf">
-    <div class="show">
-      <pdf
-        ref="pdf"
-        :src="pdfUrl"
-        :page="pageNum"
-        :rotate="pageRotate"
-        @password="password"
-        @progress="loadedRatio = $event"
-        @page-loaded="pageLoaded($event)"
-        @num-pages="pageTotalNum = $event"
-        @error="pdfError($event)"
-        @link-clicked="page = $event"
-      ></pdf>
-    </div>
-
-    <!-- <div class="pdf_footer">
-      <div class="info">
-        <div>当前页数/总页数:{{pageNum}}/{{pageTotalNum}}</div>
-      </div>
-      <div class="operate">
-        <div class="btn" @click.stop="prePage">上一页</div>
-        <div class="btn" @click.stop="nextPage">下一页</div>
-      </div>
-    </div>-->
-  </div>
-</template>
-
-<script>
-import pdf from "vue-pdf";
-export default {
-  name: "vue_pdf_preview",
-  props: {
-    // 当前pdf路径
-    pdfUrl: {
-      type: String,
-      default:
-        "https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/0629%E5%AE%9E%E6%97%B6%E8%AF%BE%E5%A0%82%E6%A8%A1%E6%8B%9F%E6%BC%94%E7%A4%BA%E8%AF%BE%E4%BB%B61656920880446.pdf",
-    },
-    ppage: {
-      type: Number,
-      default: 1,
-    },
-  },
-  components: {
-    pdf,
-  },
-  data() {
-    return {
-      // 总页数
-      pageTotalNum: 1,
-      // 当前页数
-      pageNum: 1,
-      // 加载进度
-      loadedRatio: 0,
-      // 页面加载完成
-      curPageNum: 0,
-      // 放大系数 默认百分百
-      scale: 69,
-      // 旋转角度 ‘90’的倍数才有效
-      pageRotate: 0,
-      // 单击内部链接时触发 (目前我没有遇到使用场景)
-      page: 0,
-      loading: null,
-    };
-  },
-  watch: {
-    ppage(val) {
-      this.pageNum = val;
-    },
-    pageTotalNum(val) {
-      if (val) {
-        this.loading.close();
-        // this.$parent.setData();
-        this.$parent.getRealTimeClass();
-      }
-      this.$emit("getPageTotal", val);
-    },
-  },
-  computed: {},
-  created() {},
-  mounted() {
-    this.loading = this.$loading.service({
-      background: "rgba(255, 255, 255, 0.7)",
-      target: document.querySelector(".pdf"),
-    });
-    this.pageNum = this.ppage;
-    let dwidth = document.body.offsetWidth;
-    let cwidth = document.getElementsByClassName("pdf")[0].offsetHeight * 1.77;
-    let owidth = "";
-    if (cwidth > dwidth) {
-      owidth = dwidth + "px";
-    } else {
-      owidth = cwidth + "px";
-    }
-    var a = document.getElementsByClassName("pdf")[0].offsetWidth;
-    let _this = this;
-    this.$refs.pdf.$el.style.width = owidth;
-    this.$emit("getWidth", owidth);
-    window.addEventListener("resize", () => {
-      dwidth = document.body.offsetWidth;
-      cwidth = document.getElementsByClassName("pdf")[0].offsetHeight * 1.77;
-      if (cwidth > dwidth) {
-        owidth = dwidth + "px";
-      } else {
-        owidth = cwidth + "px";
-      }
-      this.$refs.pdf.$el.style.width = owidth;
-      this.$emit("getWidth", owidth);
-    });
-  },
-  methods: {
-    //下载PDF
-    fileDownload(data, fileName) {
-      let blob = new Blob([data], {
-        //type类型后端返回来的数据中会有,根据自己实际进行修改
-        type: "application/pdf;charset-UTF-8",
-      });
-      let filename = fileName || "pdf.pdf";
-      if (typeof window.navigator.msSaveBlob !== "undefined") {
-        window.navigator.msSaveBlob(blob, filename);
-      } else {
-        var blobURL = window.URL.createObjectURL(blob);
-        // 创建隐藏<a>标签进行下载
-        var tempLink = document.createElement("a");
-        tempLink.style.display = "none";
-        tempLink.href = blobURL;
-        tempLink.setAttribute("download", filename);
-        if (typeof tempLink.download === "undefined") {
-          tempLink.setAttribute("target", "_blank");
-        }
-        document.body.appendChild(tempLink);
-        tempLink.click();
-        document.body.removeChild(tempLink);
-        window.URL.revokeObjectURL(blobURL);
-      }
-    },
-
-    //放大
-    scaleD() {
-      this.scale += 5;
-      this.$refs.pdf.$el.style.width = parseInt(this.scale) + "%";
-    },
-
-    //缩小
-    scaleX() {
-      // scale 是百分百展示 不建议缩放
-      if (this.scale == 100) {
-        return;
-      }
-      this.scale += -5;
-      console.log(parseInt(this.scale) + "%");
-      this.$refs.pdf.$el.style.width = parseInt(this.scale) + "%";
-    },
-    // 切换上一页
-    prePage() {
-      var p = this.pageNum;
-      p = p > 1 ? p - 1 : this.pageTotalNum;
-      this.pageNum = p;
-      // this.getPage(p);
-    },
-    // 切换下一页
-    nextPage() {
-      var p = this.pageNum;
-      p = p < this.pageTotalNum ? p + 1 : 1;
-      this.pageNum = p;
-      // this.getPage(p);
-    },
-    // 顺时针选中角度
-    clock() {
-      this.pageRotate += 90;
-    },
-    // 逆时针旋转角度
-    counterClock() {
-      this.pageRotate -= 90;
-    },
-    // pdf 有密码 则需要输入秘密
-    password(updatePassword, reason) {
-      updatePassword(prompt('password is "test"'));
-      console.log("...reason...");
-      console.log(reason);
-      console.log("...reason...");
-    },
-    // 页面加载成功  当前页数
-    pageLoaded(e) {
-      this.$emit("current", e);
-      this.curPageNum = e;
-    },
-    // 异常监听
-    pdfError(error) {
-      console.error(error);
-    },
-    // 打印所有
-    pdfPrintAll() {
-      this.$refs.pdf.print();
-    },
-    // 打印 第一页和第二页
-    pdfPrint() {
-      // 第一个参数 文档打印的分辨率
-      // 第二个参数 文档打印的页数
-      this.$refs.pdf.print(100, [1, 2]);
-    },
-    // 获取当前页面pdf的文字信息内容
-    logContent() {
-      this.$refs.pdf.pdf.forEachPage(function (page) {
-        return page.getTextContent().then(function (content) {
-          let text = content.items.map((item) => item.str);
-          let allStr = content.items.reduce(
-            (initVal, item) => (initVal += item.str),
-            ""
-          );
-          console.log(allStr); // 内容字符串
-          console.log(text); // 内容数组
-        });
-      });
-    },
-  },
-};
-</script>
-
-<style scoped>
-.pdf {
-  height: 100%;
-  position: relative;
-  box-sizing: border-box;
-}
-.pdf .show {
-  overflow: auto;
-  margin: auto;
-  width: 100%;
-  height: calc(100%);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-}
-
-.pdf .pdf_footer {
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  right: 0;
-  padding: 10px 0;
-  width: 100%;
-  height: 75px;
-  background-color: rgba(255, 255, 255, 0.5);
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  flex-direction: column;
-}
-.pdf .pdf_footer .info {
-  display: flex;
-  flex-wrap: wrap;
-  width: 100%;
-  justify-content: center;
-}
-/* .pdf .pdf_footer .info div {
-  width: 30%;
-} */
-.pdf .pdf_footer .operate {
-  margin: 10px 0 0;
-  display: flex;
-  flex-wrap: wrap;
-  justify-content: center;
-  width: 100%;
-}
-.pdf .pdf_footer .operate div {
-  text-align: center;
-  font-size: 15px;
-}
-.pdf .pdf_footer .operate .btn {
-  cursor: pointer;
-  margin: 5px 10px;
-  width: 100px;
-  border-radius: 10px;
-  padding: 5px;
-  color: #fff;
-  background-color: #066ebe;
-}
-</style>

+ 73 - 19
src/components/pages/classroomObservation/components/sharePdf.vue

@@ -14,13 +14,24 @@
           分享链接
         </div>
         <div class="url">
-          <el-tooltip :content="origin" placement="top" effect="dark">
-            <!-- content to trigger tooltip here -->
+					<el-input
+          class="sc_fu_input"
+          style="color: black"
+          disabled
+          v-model="origin"
+        >
+          <template slot="append"
+            ><div class="sc_fu_copyBtn" @click.stop="copy()">
+              复制链接
+            </div></template
+          >
+        </el-input>
+          <!-- <el-tooltip :content="origin" placement="top" effect="dark">
             <span>{{ origin }}</span>
           </el-tooltip>
           <span @click="copy" :data-clipboard-text="copyText" class="tag-read"
             >复制链接</span
-          >
+          > -->
         </div>
       </div>
       <div class="qrcode_box">
@@ -33,13 +44,13 @@
         </div>
       </div>
       <div class="btn_box">
-        <button
+        <!-- <button
           class="c_pub_button_confirm tag-read2"
           @click="copy2"
           :data-clipboard-text="copyText2"
         >
           复制信息
-        </button>
+        </button> -->
       </div>
     </div>
   </el-dialog>
@@ -53,7 +64,11 @@ export default {
     dialogVisibleShare: {
       type: Boolean,
       default: false
-    }
+    },
+		tid:{
+			type:String,
+			default:"",
+		}
   },
   data() {
     return {
@@ -77,7 +92,7 @@ export default {
     },
     setQr() {
       setTimeout(() => {
-        let url = 'https://beta.cloud.cocorobo.cn/#/classroomObservation';
+        let url = `https://beta.cloud.cocorobo.cn/aigpt/#/classroom_observation_board?tid=${this.tid}`;
         this.origin = url;
         this.$refs.qrCodeUrl.innerHTML = "";
         var qrcode = new QRCode(this.$refs.qrCodeUrl, {
@@ -100,17 +115,36 @@ export default {
       link.click();
     },
     copy() {
-      this.copyText = 'https://beta.cloud.cocorobo.cn/#/classroomObservation';
-      var clipboard = new Clipboard(".tag-read");
-      clipboard.on("success", e => {
-        this.$message.success("复制成功");
-        console.log("复制成功");
-        clipboard.destroy(); // 释放内存
-      });
-      clipboard.on("error", e => {
-        console.log("不支持复制,该浏览器不支持自动复制");
-        clipboard.destroy(); // 释放内存
-      });
+			const input = document.createElement("input");
+      // 设置 display为none会导致无法复制
+      // input.style.display = "none";
+      // 所以只能用其他方法隐藏
+      input.style.opacity = 0;
+      // 为了不影响布局
+      input.style.position = "fixed";
+      input.style.left = "-100%";
+      input.style.top = "-100%";
+      input.value = this.origin;
+      document.body.appendChild(input);
+      input.select();
+      const success = document.execCommand("copy");
+      document.body.removeChild(input);
+      if (!success) {
+        return this.$message.error("复制失败");
+      } else {
+        return this.$message.success("复制成功");
+      }
+      // this.copyText = `https://beta.cloud.cocorobo.cn/aigpt/#/classroom_observation_board?tid=${this.tid}`;
+      // var clipboard = new Clipboard(".tag-read");
+      // clipboard.on("success", e => {
+      //   this.$message.success("复制成功");
+      //   console.log("复制成功");
+      //   clipboard.destroy(); // 释放内存
+      // });
+      // clipboard.on("error", e => {
+      //   console.log("不支持复制,该浏览器不支持自动复制");
+      //   clipboard.destroy(); // 释放内存
+      // });
     },
     copy2() {
       let url = 'https://beta.cloud.cocorobo.cn/#/classroomObservation';
@@ -166,7 +200,12 @@ export default {
         // console.log(top.origin);
         // this.origin = top.origin
       }
-    }
+    },
+		tid(){
+			if(this.dialogVisibleShare){
+				this.setQr();
+			}
+		}
   },
   mounted() {
     // console.log("this.info", this.info);
@@ -322,4 +361,19 @@ export default {
   cursor: pointer;
   color: #0061ff;
 }
+
+.sc_fu_input /deep/.el-input__inner {
+  color: black !important;
+  cursor: text !important;
+}
+
+.sc_fu_input >>> .el-input__inner {
+  color: black !important;
+  cursor: text !important;
+}
+
+.sc_fu_copyBtn {
+  color: black;
+  cursor: pointer;
+}
 </style>

+ 2 - 2
src/components/pages/classroomObservation/components/startPage.vue

@@ -38,9 +38,9 @@
 			</div>
 			<div class="sp-m-item" @click.stop="$emit('uploadTape')">
 				<!-- <span class="sp-m-i-icon2"></span> -->
-				<div class="sp-m-item1">上传录音</div>
+				<div class="sp-m-item1">上传文件</div>
 				<div class="sp-m-item2">
-					<p>录音复盘</p>
+					<p>上传录音或实录文稿</p>
 					<p>一键分析课堂情况</p>
 				</div>
 				<div  class="sp-m-item3">

+ 6 - 15
src/components/pages/classroomObservation/components/tape.vue

@@ -105,27 +105,14 @@
 					</div>
 				</div>
 			</div>
-			<!-- <div class="t-ca-item" v-for="(item,index) in chatData" :key="index">
-				<div class="t-ca-i-left">
-					<div class="t-ca-i-l-avatar">
-						<el-avatar size="medium" :src="require('../../../../assets/icon/classroomObservation/ai.png')"></el-avatar>
-					</div>
-				</div>
-				<div class="t-ca-i-right">
-					<div class="t-ca-i-r-name">{{ item.name }}</div>
-					<div class="t-ca-i-r-time">{{ item.create_at }}</div>
-					<div class="t-ca-i-r-content">{{ item.content }}</div>
-				</div>
-			</div> -->
 		</div>
 	</div>
 </template>
 
 <script>
-import pdf from "./pdf.vue";
 export default {
 	components: {
-		pdf,
+		
 	},
 	props: {
 		chatData: {
@@ -533,6 +520,10 @@ export default {
 }
 
 .t_ca_b_ai_c_m_left>>>ol{
-	margin-left: 15px;
+	margin-left: 25px;
+}
+
+.t_ca_b_ai_c_m_left>>>ul{
+	margin-left: 25px;
 }
 </style>

+ 5 - 0
src/components/pages/classroomObservation/components/transcription.vue

@@ -6,6 +6,7 @@
 		</div> -->
 		<div class="t-content">
 			{{ data.content?data.content:"暂无..." }}
+			<span v-if="showGetTextLoading">_(转译中...)</span>
 			<!-- <div class="contentCon" v-for="(i, index) in chatList" :key="index">
 				<div class="conTim">{{ i.timer }}</div>
 				<div class="conTxt">
@@ -26,6 +27,10 @@ export default {
 				return {};
 			},
 		},
+		showGetTextLoading:{
+			type:Boolean,
+			default:false,
+		}
 	},
 	data() {
 		return {

+ 252 - 0
src/components/pages/classroomObservation/components/vpdf.vue

@@ -0,0 +1,252 @@
+<template>
+  <!--使用 pdfvuer 实现 滑动浏览 单印章-->
+  <div class="pdf">
+    <div class="loading" v-show="isloading">
+      <span>pdf可能会加载时间有点长,请耐心等待...</span>
+    </div>
+    <!-- <div id="contentArea" class="show" v-if="!loading">
+      <pdf :scale.sync="scale" :resize="true" ref="wrapper" class="p-pdf" :src="pdfData" v-for="i in numPages" :key="i"
+        :id="i" :page="i" style="width: 100%">
+      </pdf>
+    </div> -->
+    <iframe ref="viframe" style="width: 100%; height: 99%; border: none"
+      :src="'https://cloud.cocorobo.cn/pdf.js/web/viewer.html?file=' + pdfUrl"></iframe>
+    <!-- <div class="rightArea">
+      <div class="toolGroup">
+        <div class="page">第 {{ page }} / {{ numPages }} 页</div>
+        <el-button type="primary" @click.stop="prePage">上一页</el-button>
+        <el-button type="primary" @click.stop="nextPage">下一页</el-button>
+      </div>
+    </div> -->
+  </div>
+</template>
+
+<script>
+// import pdfvuer from "pdfvuer"; // pdfvuer 版本为@1.6.1
+// import "pdfjs-dist/build/pdf.worker.entry";
+// const PDF = require("pdfjs-dist");
+// PDF.GlobalWorkerOptions.workerSrc = "../../common/pdf.worker.js";
+
+export default {
+  name: "Pdfvuer",
+  components: {
+    // pdf: pdfvuer,
+  },
+  props: {
+    // 当前pdf路径
+    pdfUrl: {
+      type: String,
+      default:
+        "https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/0629%E5%AE%9E%E6%97%B6%E8%AF%BE%E5%A0%82%E6%A8%A1%E6%8B%9F%E6%BC%94%E7%A4%BA%E8%AF%BE%E4%BB%B61656920880446.pdf",
+    },
+    ppage: {
+      type: Number,
+      default: 1,
+    },
+  },
+  data() {
+    return {
+      page: 1, // 当前页
+      numPages: 0, // 总页数
+      pdfData: undefined,
+      inputPage: 1, // 输入的页码
+      isloading: false,
+      scale: "page-width",
+    };
+  },
+  mounted() {
+    // this.isloading = this.$loading.service({
+    //   background: "rgba(255, 255, 255, 0.7)",
+    //   target: document.querySelector(".loading"),
+    //   text: "加载中...",
+    //   spinner: "",
+    // });
+    this.isloading = false;
+    this.$refs.viframe.onload = function () {
+      this.isloading = false
+    }
+    console.log(this.isloading);
+    // this.getPdf();
+  },
+  beforeDestroy() { },
+  watch: {
+    pdfUrl: function (s) {
+      // this.isloading = this.$loading.service({
+      //   background: "rgba(255, 255, 255, 0.7)",
+      //   target: document.querySelector(".loading"),
+      //   text: "加载中...",
+      //   spinner: "",
+      // });
+      this.isloading = false;
+      this.$refs.viframe.onload = function () {
+        this.isloading = false
+      }
+      // this.getPdf();
+    },
+  },
+  methods: {
+    // 获取 pdf 信息
+    getPdf() {
+      this.pdfData = pdfvuer.createLoadingTask({
+        url: this.pdfUrl,
+        cMapUrl: "https://cdn.jsdelivr.net/npm/pdfjs-dist@2.16.105/cmaps/",
+        cMapPacked: true,
+      });
+      this.pdfData
+        .then((pdf) => {
+          // this.isloading.close();
+          this.isloading = false
+          this.numPages = pdf.numPages;
+        })
+        .catch((err) => {
+          console.log(err);
+        });
+    },
+    // 上一页
+    prePage() {
+      let page = this.page;
+      page = page > 1 ? page - 1 : 1;
+      this.page = page;
+    },
+    // 下一页
+    nextPage() {
+      let page = this.page;
+      page = page < this.numPages ? page + 1 : this.numPages;
+      this.page = page;
+    },
+  },
+};
+</script>
+<style scoped>
+.loading {
+  height: 100%;
+  width: 100%;
+  /* background: #000; */
+  position: absolute;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  /* background-color: rgba(255, 255, 255, 0.7); */
+  background-color: rgba(255, 255, 255, 1);
+  z-index: 9;
+  font-size: 20px;
+  color: #007fff;
+}
+
+.pdf {
+  height: 100%;
+  width: 100%;
+  position: relative;
+  box-sizing: border-box;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+.pdf .show {
+  margin: auto;
+  width: 100%;
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.pdf .show .p-pdf {
+  overflow: hidden;
+}
+
+.pdf .show .p-pdf .line {
+  position: absolute;
+  width: 50px;
+  right: -50px;
+  background: rgb(255, 186, 96);
+  height: 2px;
+}
+
+.pdf .show .p-pdf span {
+  width: 100%;
+  text-align: center;
+  color: #fff;
+}
+
+.pdf .show .p-pdf span+span {
+  margin-top: 10px;
+}
+
+.pdfbox {
+  /* border: 3px solid #000; */
+  /* box-sizing: border-box; */
+  /* border-radius: 4px; */
+  /* overflow: hidden; */
+}
+
+.pdf .pdf_footer {
+  position: sticky;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  padding: 10px 0;
+  width: 100%;
+  height: 75px;
+  background-color: rgba(255, 255, 255, 0.5);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+}
+
+.pdf .pdf_footer .info {
+  display: flex;
+  flex-wrap: wrap;
+  width: 100%;
+  justify-content: center;
+}
+
+.pdf .p-pdf .viewerContainer {
+  width: 100%;
+}
+
+/* .pdf .pdf_footer .info div {
+  width: 30%;
+} */
+.pdf .pdf_footer .operate {
+  margin: 10px 0 0;
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: center;
+  width: 100%;
+}
+
+.pdf .pdf_footer .operate div {
+  text-align: center;
+  font-size: 15px;
+}
+
+.pdf .pdf_footer .operate .btn {
+  cursor: pointer;
+  margin: 5px 10px;
+  width: 100px;
+  border-radius: 10px;
+  padding: 5px;
+  color: #fff;
+  background-color: #066ebe;
+}
+
+.pdf::-webkit-scrollbar {
+  /*滚动条整体样式*/
+  width: 6px;
+  /*高宽分别对应横竖滚动条的尺寸*/
+  height: 6px;
+}
+
+/*定义滚动条轨道 内阴影+圆角*/
+.pdf::-webkit-scrollbar {
+  border-radius: 10px;
+  background-color: #b8bdc9;
+}
+
+/*定义滑块 内阴影+圆角*/
+.pdf::-webkit-scrollbar-thumb {
+  border-radius: 10px;
+  -webkit-box-shadow: inset 0 0 6px rgb(96, 125, 184);
+  background-color: #2c5ab3;
+}
+</style>

+ 5 - 1
src/components/pages/classroomObservation/components/wangEnduit.vue

@@ -47,7 +47,11 @@ export default {
     placeholder: {
       type: String,
       default: "请输入正文"
-    }
+    },
+		showGetTextLoading:{
+			type:Boolean,
+			default:false,
+		}
   },
   watch: {
     isClear(val) {

+ 18 - 5
src/components/pages/classroomObservation/index.vue

@@ -61,18 +61,24 @@
 				</div>
 				<div class="co-h2-r-btn2" @click.stop="preview()">
 					<!-- <span class="co-h2-r-b-icon2"></span> -->
-					<img
+					<el-tooltip class="item" effect="light" content="预览" placement="top">
+						<img
 						src="../../../assets/icon/classroomObservation/Syan.png"
 						alt=""
 					/>
+    			</el-tooltip>
+					
 					<!-- <div>预览</div> -->
 				</div>
 				<div class="co-h2-r-btn2" @click.stop="shareBtn">
 					<!-- <span class="co-h2-r-b-icon3"></span> -->
-					<img
+					<el-tooltip class="item" effect="light" content="分享" placement="top">
+						<img
 						src="../../../assets/icon/classroomObservation/daoChu.png"
 						alt=""
 					/>
+    </el-tooltip>
+					
 					<!-- <div>分享</div> -->
 				</div>
 			</div>
@@ -103,10 +109,11 @@
 		</div>
 		<sharePdf
 			:dialogVisibleShare="dialogVisibleShare"
+			:tid="tid"
 			@shareBtn="shareBtn"
 		></sharePdf>
 
-		<addNewCourseDialog ref="addNewCourseDialogRef" @success="addNewCourse" />
+		<addNewCourseDialog :courseList="optionData" ref="addNewCourseDialogRef" @success="addNewCourse" />
 	</div>
 </template>
 
@@ -147,9 +154,11 @@ export default {
 			});
 		},
 		addNewCourse(form) {
+			if(this.loading)return this.$message.info("请稍等")
+			this.loading = true;
 			let params = {
 				tid: form.no,
-				type: "10",
+				name:form.name
 			};
 			this.ajax
 				.post(
@@ -157,7 +166,6 @@ export default {
 					params
 				)
 				.then((res) => {
-					console.log(res);
 					let _data = res.data.FunctionResponse;
 					if (_data.message == "创建成功") {
 						this.tid = form.no;
@@ -172,6 +180,7 @@ export default {
 							.then((res2) => {
 								let _data2 = res2.data.FunctionResponse;
 								if (_data2.message == "创建成功") {
+									this.loading = false;
 									this.$nextTick(() => {
 										this.getCourseList().then((_) => {
 											this.getFileIdId();
@@ -185,8 +194,10 @@ export default {
 							});
 					} else if (_data.message == "tid重复") {
 						this.$message.error("该课程已存在");
+						this.loading = false;
 					} else {
 						this.$message.error("创建失败");
+						this.loading = false;
 					}
 				});
 		},
@@ -299,6 +310,7 @@ export default {
 			this.$refs.chatAreaRef.changeAudioUrl(data.url);
 		},
 		getCourseList() {
+			this.loading = true;
 			return new Promise((resolve, reject) => {
 				this.ajax
 					.post("https://gpt4.cocorobo.cn/get_classroom_observation_all")
@@ -322,6 +334,7 @@ export default {
 						if (this.optionData.length > 0) {
 							if (!this.tid) this.tid = this.optionData[0].value;
 						}
+						this.loading = false;
 						resolve();
 					});
 			});

+ 1 - 1
src/components/pages/course.vue

@@ -32,7 +32,7 @@
               <!-- <div type="primary" @click="goToCourse2()">任务式课程</div> -->
               <!-- <div type="primary" @click="goToCourse()">阶段式课程</div> -->
               <div type="primary" @click="goToCourse4()">阶段式课程</div>
-              <div type="primary" @click="goToCourse5()">AI模式</div>
+              <div type="primary" @click="goToCourse5()" v-show="org == '1973f6c7-1561-11ee-91d8-005056b86db5'">AI模式</div>
             </div>
           </button>
         </div>

+ 538 - 0
src/components/pages/pblCourse/component/chatArea.vue

@@ -0,0 +1,538 @@
+<template>
+	<div class="chat" v-loading="loading">
+		<div class="c_chat" ref="chatRef">
+			<div class="c_c_item" v-for="item in chatList" :key="item.uid">
+				<div class="c_c_i_user" v-if="item.content">
+					<div class="c_c_i_u_message">
+						<div class="c_c_i_u_m_top">
+							<span class="chatTime">{{ item.createtime }}</span>
+							<span>科科</span>
+						</div>
+						<div class="c_c_i_u_m_bottom">
+							<span class="coptText" @click="copyText(item.aiContent)"></span>
+							<div
+								class="c_c_i_u_m_content chatContent"
+								v-html="item.content"
+							></div>
+						</div>
+					</div>
+					<div class="c_c_i_u_avatar">
+						<el-avatar
+							style="width: 100%; height: 100%"
+							:src="require('../../../../assets/icon/pblCourse/userAvatar.png')"
+							fit="fit"
+						></el-avatar>
+					</div>
+				</div>
+				<div class="c_c_i_ai">
+					<div class="c_c_i_ai_avatar">
+						<el-avatar
+							style="width: 100%; height: 100%"
+							:src="
+								require('../../../../assets/icon/pblCourse/aiTeacherAvatar.png')
+							"
+							fit="fit"
+						></el-avatar>
+					</div>
+					<div class="c_c_i_ai_message">
+						<div class="c_c_i_ai_m_top">
+							<span>小可老师</span>
+							<span class="chatTime">{{item.createtime}}</span>
+						</div>
+						<div class="c_c_i_ai_m_bottom">
+							<div
+								v-loading="item.loading"
+								class="c_c_i_ai_m_content chatContent"
+								v-html="item.aiContent"
+							></div>
+							<span class="coptText" @click="copyText(item.aiContent)"></span>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+		<!-- <div class="c_controls">
+			<span class="c_controls_item" @click="clearChat">清空对话</span>
+		</div> -->
+		<div class="c_bottom" 	v-loading="chatLoading">
+			<!-- <div class="c_b_record">
+				<span></span>
+			</div> -->
+			<div class="c_b_inputArea">
+				<el-input
+					class="c_b_input"
+					@keyup.enter.native="send()"
+					v-model="textValue"
+				
+				>
+				</el-input>
+				<!-- <span></span> -->
+			</div>
+			<div class="c_b_send" @click="send">
+				<span></span>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script>
+import { v4 as uuidv4 } from "uuid";
+import MarkdownIt from "markdown-it";
+export default {
+	data() {
+		return {
+			loading: false,
+			chatLoading:false,
+			userId:this.$route.query['userid'],
+			chatList: [],
+			textValue: "",
+		};
+	},
+	methods: {
+		// 发送消息
+		send() {
+			if(this.chatLoading || this.loading)return this.$message.info("请稍等...")
+			if(!this.textValue.trim())return this.$message.info("请输入内容");
+			const _uuid = uuidv4();
+			this.chatLoading = true;
+			this.chatList.push({
+				role: "user",
+				content: `${this.textValue}`,
+				uid: _uuid,
+				AI: "AI",
+				aiContent: "",
+				oldContent: "",
+				filename: "",
+				index: this.chatList.length,
+				createtime:new Date().toLocaleString().replaceAll("/",'-'),
+				loading: true,
+			})
+			
+			let params = {
+				model: "gpt-3.5-turbo",
+					temperature: 0,
+					max_tokens: 4096,
+					top_p: 1,
+					frequency_penalty: 0,
+					presence_penalty: 0,
+					messages: [{role:"user",content:this.textValue}],
+					uid: _uuid,
+					mind_map_question: "",
+			}
+			this.scrollBottom();
+			this.textValue = "";
+			this.ajax
+					.post("https://gpt4.cocorobo.cn/chat", params)
+					.then((res) => {
+						if (res.data.FunctionResponse.result == "发送成功") {
+						} else {
+							this.chatLoading = false;
+							this.$message.warning(res.data.FunctionResponse.result);
+						}
+					})
+					.catch((e) => {
+						this.chatLoading = false;
+						console.log(e);
+					});
+			// this.$message.info(`发送:${this.textValue}`);
+			// this.textValue = "";
+			this.getAiContent(_uuid)
+		},
+		getAiContent(_uid) {
+			let _source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/     https://gpt4.cocorobo.cn/stream/
+			let _allText = "";
+			let _mdText = "";
+			const md = new MarkdownIt();
+			_source.onmessage = (_e) => {
+				if (_e.data.replace("'", "").replace("'", "") == "[DONE]") {
+					//对话已经完成
+					_mdText = _mdText.replace("_", "");
+					_source.close();
+					this.chatList.find((i) => i.uid == _uid).aiContent = _mdText;
+					this.chatList.find((i) => i.uid == _uid).isalltext = true;
+					this.chatList.find((i) => i.uid == _uid).isShowSynchronization = true;
+					this.chatList.find((i) => i.uid == _uid).loading = false;
+					this.chatLoading = false;
+					this.insertChat(_uid);
+					return;
+				} else {
+					//对话还在继续
+					let _text = "";
+					_text = _e.data.replaceAll("'", "");
+					if (_allText == "") {
+						_allText = _text.replace(/^\n+/, ""); //去掉回复消息中偶尔开头就存在的连续换行符
+					} else {
+						_allText += _text;
+					}
+					_mdText = _allText + "_";
+					_mdText = _mdText.replace(/\\n/g, "\n");
+					_mdText = _mdText.replace(/\\/g, "");
+					if (_allText.split("```").length % 2 == 0) _mdText += "\n```\n";
+					//转化返回的回复流数据
+					_mdText = md.render(_mdText);
+					this.chatList.find((i) => i.uid == _uid).aiContent = _mdText;
+					this.chatList.find((i) => i.uid == _uid).loading = false;
+					this.scrollBottom();
+					// 处理流数据
+				}
+			};
+		},
+		// 复制
+		copyText(_html) {
+			const div = document.createElement("div")
+			div.innerHTML = _html
+			const _text = div.innerText
+			navigator.clipboard.writeText(_text).then(_=>{
+			  this.$message.success("复制成功")
+			}, function(err) {
+				this.$message.error(`无法复制:${err}`)
+			});
+		},
+		// 保存对话
+		//保存消息
+		insertChat(_uid) {
+			let _data = this.chatList.find((i) => i.uid == _uid);
+			if (!_data) return;
+			let params = {
+				userId: this.userId,
+				userName: "qgt",
+				groupId: "602def61-005d-11ee-91d8-005056b8q12w",
+				answer: _data.aiContent,
+				problem: _data.content,
+				file_id: "",
+				alltext: _data.aiContent,
+				type: "chat",
+				filename: _data.filename,
+				session_name: `${this.userId}-pblCourse`,
+			};
+			this.ajax
+				.post("https://gpt4.cocorobo.cn/insert_chat", params)
+				.then((res) => {
+					console.log("保存对话")
+				});
+		},
+		// 获取对话记录
+		getChatList() {
+			return new Promise((resolve, reject) => {
+				this.chatList = [];
+				this.loading = true;
+				this.chatLoading = true;
+				let params = {
+					userid: this.userId,
+					groupid: "602def61-005d-11ee-91d8-005056b8q12w",
+					// session_name:``
+					session_name: `${this.userId}-pblCourse`,
+				};
+				this.ajax
+					.post("https://gpt4.cocorobo.cn/get_agent_park_chat", params)
+					.then((res) => {
+						let _data = JSON.parse(res.data.FunctionResponse);
+						if (_data.length > 0) {
+							console.log(_data)
+							let _chatList = [];
+							for (let i = 0; i < _data.length; i++) {
+								_chatList.push({
+									loading: false,
+									role: "user",
+									content: _data[i].problem,
+									uid: _data[i].id,
+									AI: "AI",
+									aiContent: _data[i].answer,
+									oldContent: _data[i].answer,
+									isShowSynchronization: false,
+									filename: _data[i].filename,
+									index: i,
+									is_mind_map: false,
+									fileid: _data[i].fileid,
+									createtime:_data[i].createtime
+								});
+							}
+							this.chatList = _chatList;
+							this.chatLoading = false;
+							this.loading = false;
+						} else {
+							//没有对话记录
+							this.loading = false;
+							this.chatLoading = false;
+						}
+						resolve();
+						this.scrollBottom();
+					})
+					.catch((err) => {
+						console.log(err);
+						this.$message.error("获取对话记录失败");
+						this.chatLoading = false;
+						this.scrollBottom();
+						resolve();
+					});
+			});
+		},
+		//对话触底
+		scrollBottom(){
+			this.$nextTick(()=>{
+				this.$refs.chatRef.scrollTop = this.$refs.chatRef.scrollHeight;
+			})
+		},
+		//清除对话
+		clearChat(){
+			this.chatList = [];
+			this.scrollBottom();
+		},
+	},
+
+	mounted() {
+		this.getChatList().then(_=>{
+			this.scrollBottom();
+		})
+	},
+};
+</script>
+
+<style scoped>
+.chat {
+	width: 100%;
+	height: 100%;
+	background-color: #ffffff;
+	border-radius: 12px;
+	box-sizing: border-box;
+	border: solid 1px #f3f7fd;
+	box-shadow: 0 4px 10px 0 #1d388321;
+}
+
+.c_chat {
+	width: 100%;
+	height: calc(100% - 56px);
+	box-sizing: border-box;
+	padding: 15px;
+	overflow: auto;
+}
+
+.c_c_item {
+	background-color: none;
+}
+
+.c_c_i_user {
+	width: 100%;
+	height: auto;
+	display: flex;
+	justify-content: flex-end;
+	align-items: flex-start;
+	margin-bottom: 10px;
+}
+
+.c_c_i_u_message {
+	height: auto;
+	width: auto;
+	max-width: calc(100% - 50px - 40px);
+	display: flex;
+	flex-direction: column;
+	align-items: flex-end;
+	margin-right: 5px;
+}
+.c_c_i_u_m_top {
+	margin-bottom: 10px;
+	display: flex;
+	align-items: flex-end;
+}
+
+.c_c_i_u_m_top > span {
+	margin-left: 5px;
+}
+
+.c_c_i_u_m_bottom {
+	display: flex;
+	align-items: flex-end;
+}
+
+.coptText {
+	min-width: 15px;
+	min-height: 15px;
+	display: block;
+	background: url("../../../../assets/icon/pblCourse/copyIcon.png") no-repeat;
+	background-size: 100% 100%;
+	margin: 0px 10px 6px 10px;
+	cursor: pointer;
+}
+
+.c_c_i_u_m_content {
+	width: auto;
+	height: auto;
+	padding: 10px;
+	background-color: #e2eeff;
+	border-radius: 8px 2px 8px 8px;
+	border: solid 1px #000000e5;
+}
+
+.c_c_i_u_avatar {
+	width: 45px;
+	height: 45px;
+	margin: 0px 0 0 10px;
+}
+
+.c_c_i_ai {
+	margin-top: 10px;
+	width: 100%;
+	height: auto;
+	display: flex;
+	align-items: flex-start;
+	margin-bottom: 10px;
+}
+
+.c_c_i_ai_avatar {
+	width: 45px;
+	height: 45px;
+	margin: 0px 0 0 10px;
+}
+
+.c_c_i_ai_message {
+	height: auto;
+	width: auto;
+	max-width: calc(100% - 50px - 40px);
+	display: flex;
+	flex-direction: column;
+	align-items: flex-start;
+	margin-left: 5px;
+}
+.c_c_i_ai_m_top {
+	display: flex;
+	align-items: flex-end;
+	margin-bottom: 10px;
+}
+
+.c_c_i_ai_m_top > span {
+	margin-left: 5px;
+}
+
+.c_c_i_ai_m_bottom {
+	display: flex;
+	align-items: flex-end;
+}
+
+.c_c_i_ai_m_content {
+	min-width: 40px;
+	min-height: 25px;
+	width: auto;
+	height: auto;
+	padding: 10px;
+	background-color: #ffffff;
+	border-radius: 2px 8px 8px 8px;
+	border: solid 1px #000000e5;
+}
+
+.chatContent >>> ol {
+	margin-left: 25px;
+}
+
+.chatContent >>> ul {
+	margin-left: 25px;
+}
+
+.chatTime{
+	font-size: 14px;
+	margin: 0 10px;
+	color: #2c2f3b;
+}
+
+.c_controls{
+	width: 100%;
+	height: 30px;
+}
+
+.c_controls>span{
+	max-height: 30px;
+	width: auto;
+	padding: 5px 10px;
+	font-size: 14px;
+	border-radius: 15px;
+	box-shadow: 0 0 2px 0px gray;
+	margin-left: 10px;
+	cursor: pointer;
+}
+
+.c_controls>span:hover{
+	background-color: #f3f7fd;
+}
+
+.c_bottom {
+	width: 100%;
+	height: 56px;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+	border-top: solid 0.5px #e7e7e7;
+}
+
+.c_b_record {
+	width: 30px;
+	height: 30px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+
+.c_b_record > span {
+	width: 24px;
+	height: 24px;
+	background-image: url("../../../../assets/icon/pblCourse/recordIcon.png");
+	background-size: 100% 100%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+
+.c_b_inputArea {
+	width: calc(100% - 100px);
+	background-color: #f3f3f3;
+	border-radius: 50px;
+	height: 80%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	margin: 0 15px;
+}
+
+.c_b_input {
+	width: calc(100% - 40px);
+	margin-right: 10px;
+}
+
+.c_b_input >>> .el-input__inner {
+	border: none;
+	background-color: #f3f3f3;
+	outline: none;
+	border-radius: 50px 0 0 50px;
+	font-size: 1.2em;
+}
+
+.c_b_inputArea > span {
+	width: 24px;
+	height: 24px;
+	background-image: url("../../../../assets/icon/pblCourse/fileIcon.png");
+	background-size: 100% 100%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	margin-right: 10px;
+	cursor: pointer;
+}
+
+.c_b_send {
+	width: 40px;
+	height: 40px;
+	border-radius: 50%;
+	background-color: #3681fc;
+	cursor: pointer;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	cursor: pointer;
+}
+
+.c_b_send > span {
+	width: 60%;
+	height: 60%;
+	background-image: url("../../../../assets/icon/pblCourse/sendIcon.png");
+	background-size: 100% 100%;
+}
+</style>

+ 185 - 0
src/components/pages/pblCourse/component/doWorkArea.vue

@@ -0,0 +1,185 @@
+<template>
+	<div class="doWork">
+		<div class="dw_header">
+			<div class="dw_h_left">
+				<img :src="require('../../../../assets/icon/pblCourse/phaseIcon.png')"> 
+				<span>阶段{{ phase.atPhase+1 }}:{{ task?task.name:'' }}</span>
+			</div>
+			<div class="dw_h_right">
+				<span class="dw_h_r_back" @click.stop="back()"></span>
+				<span class="dw_h_r_down" @click.stop="down()"></span>
+			</div>
+		</div>
+		<div class="dw_work">
+			<work v-if="task" :task="task" @submitTask="down" @choiceAnswer="choiceAnswer" @getTaskList="getTaskList" :phase="phase"/>
+			<div v-else>
+				<h3>等待生成数据...</h3>
+			</div>
+		</div>
+		<div class="dw_bottom">
+			<div class="dw_b_btn" @click.stop="submitTask()">
+				<img :src="require('../../../../assets/icon/pblCourse/bookIcon.png')">
+				<span>提交作业</span>
+			</div>
+		</div>	
+	</div>
+</template>
+
+<script>
+import work from './work'
+	export default {
+		emits:["changePhase","choiceAnswer","submitTask","getTaskList"],
+		props:{
+			phase:{
+				type:Object,
+				default:()=>{
+					return {
+						doPhase:0,
+						atPhase:0,
+					}
+				}
+			},
+			task:{
+				type:Object,
+				require:true,
+			},
+		},
+		computed:{
+			isOk(){
+				return !(this.phase.doPhase>this.phase.atPhase)
+			}
+		},
+		components:{
+			work,
+		},
+		methods:{
+			submitTask(){
+				this.$emit("submitTask")
+			},
+			down(){
+				if(this.isOk)return this.submitTask();
+				if(this.phase.atPhase>=4)return this.$message.info("已经是最后一个阶段啦")
+				this.$emit("changePhase",'atPhase',(this.phase.atPhase+1))
+			},
+			back(){
+				if(this.phase.atPhase<=0)return this.$message.info("已经是第一个阶段啦")
+				this.$emit("changePhase",'atPhase',(this.phase.atPhase-1))
+			},
+			choiceAnswer(arr){
+				this.$emit("choiceAnswer",arr)
+			},
+			getTaskList(_data){
+				this.$emit("getTaskList",_data)
+			}
+		}
+	}
+</script>
+
+<style scoped>
+.doWork{
+	width: 100%;
+	height: 100%;
+	border-radius: 12px;
+	overflow: hidden;
+	background-color: white;
+}
+
+.dw_header{
+	width: 100%;
+	height: 60px;
+	background-color: #FFF3EA;
+	margin-bottom: 15px;
+	display: flex;
+	justify-content: space-between;
+	align-items: center;
+	box-sizing: border-box;
+	padding: 0 20px;
+}
+
+.dw_h_left{
+	display: flex;
+	align-items: center;
+}
+
+.dw_h_left>img{
+	width: 40px;
+	height: 40px;
+	margin-right: 10px;
+}
+
+
+.dw_h_left>span{
+	font-size: 22px;
+	font-weight: bold;
+}
+
+
+.dw_h_right{
+	display: flex;
+	align-items: center;
+}
+
+.dw_h_right>span{
+	display: block;
+	width: 30px;
+	height: 30px;
+	background: url("../../../../assets/icon/pblCourse/backIcon.png") no-repeat;
+	background-size: 100% 100%;
+	margin-right: 10px;
+	margin-left: 5px;
+	cursor: pointer;
+}
+
+.dw_h_r_down{
+	transform: rotate(180deg);
+}
+.dw_work{
+	width: calc(100% - 30px);
+	height: calc(100% - 60px - 100px - 30px);
+	box-sizing: border-box;
+	margin: 0 15px;
+	border-radius: 8px;
+	background-color: #F3F7FD;
+}
+
+.dw_bottom{
+	margin-top: 15px;
+	width: 100%;
+	height: 100px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	box-sizing: border-box;
+	padding: 0 15px 15px 15px;
+}
+
+.dw_b_btn{
+	width: 100%;
+	height: 100%;
+	background-color: #F3F7FD;
+	border-radius: 8px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	transition: .3s;
+	cursor: pointer;
+}
+
+.dw_b_btn:hover{
+	background-color: #eaeef5;
+}
+
+.dw_b_btn>img{
+	width: 50px;
+	height: 50px;
+	margin-right: 10px;
+	cursor: pointer;
+}
+
+.dw_b_btn>span{
+	font-size: 22px;
+	background: linear-gradient(to right, #3673E8, #AD88FD);
+	-webkit-background-clip: text;
+	color: transparent;
+}
+</style>

+ 158 - 0
src/components/pages/pblCourse/component/procedureArea.vue

@@ -0,0 +1,158 @@
+<template>
+	<div class="procedure">
+		<div class="title">5EX挑战</div>
+		<div class="content" ref="content">
+			<div class="procedure_content" v-for="(i, index) in 5" :key="index" :class="{active: phase.atPhase >= index}">
+				<i class="img" :class="{ isDo : phase.doPhase > index}"></i>
+				<i class="dot"></i>
+				<span class="name">
+					<span>阶段{{ index+1 }}</span>
+				</span>
+				<div class="stepBorder" v-if="i != 5" :style="{width: `calc(${contentWidth} / (${5 - 1}))`}" :class="{active: phase.doPhase > index}"></div>
+			</div>
+			
+		</div>
+	</div>
+</template>
+
+<script>
+	export default {
+		props:{
+			phase:{
+				type:Object,
+				default:()=>{
+					return {
+						doPhase:0,
+						atPhase:0,
+					}
+				}
+			}
+		},
+		data() {
+			return {
+				contentWidth:"100px",
+			}
+		},
+		mounted () {
+			this.$nextTick(() => {
+				this.contentWidth = this.$refs.content.offsetWidth + "px";
+    			window.addEventListener("resize", () => {
+					this.contentWidth = this.$refs.content.offsetWidth + "px";
+				})
+			})
+		},
+		methods: {
+
+		},
+	}
+</script>
+
+<style scoped>
+.procedure{
+	width: 100%;
+	height: 100%;
+	background-image: url('../../../../assets/icon/pblCourse/top_pbl.png');
+	background-size: 100% 100%;
+    border-radius: 15px;
+	position: relative;
+	display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+
+.procedure .title{
+	position: absolute;
+    top: 0;
+    color: #fff;
+    background: linear-gradient(90deg, rgba(54, 115, 232, 0) 0%, #727EF3 50%, rgba(54, 115, 232, 0) 100%);
+    width: 190px;
+    height: 24px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    font-size: 14px;
+}
+.procedure .content{
+	display: flex;
+    width: calc(100% - 40px);
+    margin: 0 auto;
+    justify-content: space-between;
+	position: relative;
+}
+.content .procedure_content{
+	display: flex;
+    flex-direction: column;
+    align-items: center;
+	position: relative;
+}
+.content .procedure_content > .img.isDo{
+	background-image: url('../../../../assets/icon/pblCourse/top_book_active.png');
+}
+.content .procedure_content > .img{
+	background-image: url('../../../../assets/icon/pblCourse/top_book.png');
+    display: block;
+    width: 40px;
+    height: 40px;
+    background-size: 100% 100%;
+}
+.content .procedure_content > .dot{
+	border: 2px solid #c8aeff;
+    width: 16px;
+    height: 16px;
+    border-radius: 28px;
+    margin-bottom: 10px;
+	z-index: 1;
+	background: #FFFFFF59;
+	box-sizing: border-box;
+}
+
+.content .procedure_content.active > .dot{
+	border: none;
+    background: linear-gradient(90deg, #3673E8 0%, #AD88FD 100%);
+}
+
+.content .procedure_content > .name{
+	background: rgba(255, 255, 255, .55);
+	border: 2px solid #c8aeff;
+    display: block;
+    border-radius: 49px;
+    font-size: 16px;
+    padding: 3px 7px;
+    font-weight: 600;
+	position: relative;
+	overflow: hidden;
+}
+
+.content .procedure_content.active > .name{
+	border: none;
+    background: linear-gradient(90deg, #3673E8 0%, #AD88FD 100%);
+}
+
+.content .procedure_content > .name > span{
+	background-image: linear-gradient(90deg, #3673E8 0%, #AD88FD 100%);
+    background-clip: text;
+    color: transparent;
+}
+
+.content .procedure_content.active > .name > span{
+	background-image: unset;
+    background-clip: unset;
+    color: #fff;
+}
+
+.content .procedure_content > .stepBorder{
+	position: absolute;
+    width: 100%;
+    height: 10px;
+    border: 1px solid #FFFFFF59;
+    background: #FFFFFF8C;
+    left: 22px;
+    top: 43px;
+    box-sizing: border-box;
+}
+
+.content .procedure_content > .stepBorder.active{
+	background: linear-gradient(90deg, #3673E8 0%, #AD88FD 100%);
+}
+</style>

+ 75 - 0
src/components/pages/pblCourse/component/selectTopicDialog.vue

@@ -0,0 +1,75 @@
+<template>
+  <div>
+    <el-dialog
+      title="选题设置"
+      :visible.sync="dialogVisible"
+      width="500px"
+			:show-close="false"
+			:close-on-click-modal="false"
+			:close-on-press-escape="false"
+      v-loading="loading"
+    >
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-form-item label="选题:" prop="title">
+          <el-input v-model="form.title"></el-input>
+        </el-form-item>
+			</el-form>
+      <span slot="footer" class="shareFooter">
+        <el-button type="primary" @click="submitFrom('form')">确定</el-button>
+      </span>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      dialogVisible: false,
+      loading: false,
+      form: {
+        title:"",
+      },
+			rules:{
+				title:[
+					{ required: true, message: '请输入选题', trigger: 'blur' },
+				]
+			}
+    };
+  },
+  watch: {
+    
+  },
+  methods: {
+    open() {
+      this.dialogVisible = true;
+    },
+    close() {
+      this.dialogVisible = false;
+      this.form = {
+        title:""
+      };
+    },
+		submitFrom(formName){
+			this.$refs[formName].validate((valid) => {
+        if (valid) {
+          this.$emit("success",this.form);
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+		},
+
+  }
+};
+</script>
+
+<style scoped>
+.shareContent {
+  width: 500px;
+  max-width: 500px;
+  height: auto;
+}
+
+</style>

+ 335 - 0
src/components/pages/pblCourse/component/work.vue

@@ -0,0 +1,335 @@
+<template>
+	<div class="work" ref="workRef">
+		<div class="w_nowWork">
+			<div class="w_nw_header">
+				<div class="w_nw_h_title">
+					<img :src="require('../../../../assets/icon/pblCourse/taskIcon.png')">  
+					<span>当前任务</span>
+				</div>
+				<div class="w_nw_h_btn" @click.stop="changeTask" v-if="!(phase.doPhase>phase.atPhase)">
+					<img :src="require('../../../../assets/icon/pblCourse/changeIcon.png')">
+					<span>更换任务</span>
+				</div>
+			</div>
+			<div class="w_nw_introduce">
+				<div v-html="htmlText(task.target)" style="font-weight: bold;"></div>
+				<div v-html="htmlText(task.detail)"></div>
+				<div v-html="htmlText(task.steps)"></div>
+				<div v-html="htmlText(task.tips)"></div>
+			</div>
+		</div>
+		<div class="w_doWork">
+			<div class="w_dw_header">
+				<div class="w_dw_h_title">
+					<img :src="require('../../../../assets/icon/pblCourse/doWorkIcon.png')">  
+					<span>通关挑战({{taskIndex+1}}/{{task.answerArray?task.answerArray.length:5}})</span>
+				</div>
+				<div class="w_dw_h_controls">
+					<span @click.stop="back()" :class="[taskIndex==0?'w_dw_h_c_disabled':'']">上一题</span>
+					<span @click.stop="down()" v-show="taskIndex!=4">下一题</span>
+					<span @click.stop="submitTask()" class="w_dw_h_c_submit" v-show="taskIndex==4">提交</span>
+				</div>
+			</div>
+			<div class="w_dw_work">
+					<span class="w_dw_w_title">{{ taskIndex+1 }}.{{ task.answerArray?task.answerArray[taskIndex].title:"" }}</span>
+					<div class="w_dw_w_radio">
+						<el-radio-group class="w_dw_w_r_group" v-model="task.answerArray[taskIndex].userAnswer" size="medium" @input="choiceAnswer">
+  					  <el-radio class="w_dw_w_r_g_item" v-for="(item,index) in task.answerArray?task.answerArray[taskIndex].option:[]" :key="index+''+taskIndex" size="medium " :label="index" @input="choiceAnswer">{{ item }}</el-radio>
+  					</el-radio-group>
+					</div>
+				</div>
+		</div>
+	</div>
+</template>
+
+<script>
+	import MarkdownIt from "markdown-it";
+	export default {
+		emits:['choiceAnswer','submitTask',"getTaskList"],
+		props:{
+			task:{
+				type:Object,
+				default:()=>{
+					return{
+						answerArray:[
+							{}
+						]
+					}
+				},
+				
+			},
+			phase:{
+				type:Object,
+				default:()=>{
+					return {
+						doPhase:0,
+						atPhase:0,
+					}
+				}
+			},
+
+		},
+		data(){
+			return{
+				taskIndex:0,
+				detail:"",
+				steps:"",
+				target:"",
+				tips:"",
+			}
+		},
+		computed:{
+			htmlText(){
+				return (mdText)=>{
+					const md = new MarkdownIt();
+					return md.render(mdText)
+				}
+			}
+		},
+		watch:{
+			task(){
+				this.taskIndex = 0;
+				this.$refs.workRef?this.$refs.workRef.scrollTop = 0:'';
+				// this.$refs.workRef.scrollTop = 0;
+			}
+		},
+		methods:{
+			changeTask(){
+				this.$emit("getTaskList",this.phase.atPhase)
+			},
+			back(){
+				if(this.taskIndex==0)return;
+				this.taskIndex-=1;
+			},
+			down(){
+				if(this.taskIndex>=(this.task?this.task.answerArray.length-1:5))return;
+				this.taskIndex+=1;
+			},
+			choiceAnswer(_index){
+				this.$emit("choiceAnswer",[this.taskIndex,_index])
+				this.$forceUpdate();
+			},
+			submitTask(){
+				if((this.phase.doPhase>this.phase.atPhase) || this.phase.doPhase>=5)return this.$message.error("该阶段已提交过了")
+				this.$emit("submitTask")
+			}
+		}
+	}
+</script>
+
+<style scoped>
+.work{
+	width: 100%;
+	max-height: 100%;
+	overflow: auto;
+	box-sizing: border-box;
+	padding: 25px;
+	/* background-color: aqua; */
+}
+
+.w_nowWork{
+	width: 100%;
+	height: auto;
+	margin-bottom: 20px;
+}
+
+.w_nw_header{
+	width: 100%;
+	display: flex;
+	justify-content: space-between;
+	height: 35px;
+	margin-bottom: 20px;
+}
+
+.w_nw_h_title{
+	display: flex;
+	align-items: center;
+}
+
+.w_nw_h_title>img{
+	width: 50px;
+	height: 50px;
+	margin-right: 10px;
+}
+
+.w_nw_h_title>span{
+	font-size: 24px;
+	font-weight: bold;
+	background: linear-gradient(to right, #3673E8, #AD88FD);
+	-webkit-background-clip: text;
+	color: transparent;
+}
+
+.w_nw_h_btn{
+	width: auto;
+	height: 100%;
+	border-radius: 100px;
+	box-sizing: border-box;
+	border: solid 1px #AD88FD;
+	background-color: white;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	padding: 0px 20px 0 10px;
+	cursor: pointer;
+	transition: .3s;
+}
+
+.w_nw_h_btn:hover{
+	background-color: rgb(248, 246, 246);
+}
+
+.w_nw_h_btn>img{
+	width: 20px;
+	height: 20px;
+	margin-right: 10px;
+}
+
+.w_nw_h_btn>span{
+	font-size: 16px;
+}
+
+.w_nw_introduce>div{
+	margin: 10px 0px;
+}
+
+.w_nw_introduce >>> ol{
+	margin-left: 25px;
+}
+
+.w_nw_introduce >>> ul {
+	margin-left: 25px;
+}
+
+.w_nw_introduce >>> h2{
+	margin-top: 10px;
+}
+.w_nw_introduce >>> h3{
+	margin-top: 10px;
+}
+.w_nw_introduce >>> h4{
+	margin-top: 10px;
+}
+.w_nw_introduce >>> h5{
+	margin-top: 10px;
+}
+.w_nw_introduce >>> h6{
+	margin-top: 10px;
+}
+
+
+
+.w_doWork{
+	width: 100%;
+	height: auto;
+}
+
+.w_dw_header{
+	width: 100%;
+	display: flex;
+	justify-content: space-between;
+	height: 35px;
+	margin-bottom: 20px;
+}
+
+.w_dw_h_title{
+	display: flex;
+	align-items: center;
+}
+
+.w_dw_h_title>img{
+	width: 50px;
+	height: 50px;
+	margin-right: 10px;
+}
+
+.w_dw_h_title>span{
+	font-size: 24px;
+	font-weight: bold;
+	background: linear-gradient(to right, #3673E8, #AD88FD);
+	-webkit-background-clip: text;
+	color: transparent;
+}
+
+.w_dw_h_controls{
+	display: flex;
+	align-items: center;
+}
+
+.w_dw_h_c_disabled{
+	opacity: .5;
+	cursor: not-allowed !important;
+}
+
+.w_dw_h_c_submit{
+	background:linear-gradient(to right,#6082E5,#A293F3);
+	color: white;
+	padding: 0 23px !important;
+	/* 不可选中 */
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+
+
+.w_dw_h_controls>span{
+	font-size: 16px;
+	margin-left: 20px;
+	cursor: pointer;
+	transition: .3s;
+	box-sizing: border-box;
+	padding: 0 15px;
+	background-color: white;
+	height: 100%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	border-radius: 100px;
+	border: solid 1px #AD88FD;
+	/* 不可选中 */
+	-webkit-user-select: none;
+	-moz-user-select: none;
+	-ms-user-select: none;
+	user-select: none;
+}
+
+.w_dw_h_controls>span:hover{
+	background-color: rgb(248, 246, 246);
+}
+
+.w_dw_work{
+	width: 100%;
+	height: auto;
+
+}
+
+.w_dw_w_title{
+	font-size: 24px;
+	font-weight: bold;
+}
+
+.w_dw_w_radio{
+	margin-top: 10px;
+}
+
+.w_dw_w_r_group{
+	display: flex;
+	flex-direction: column;
+	margin-top: 10px;
+}
+
+.w_dw_w_r_g_item{
+	margin-top: 15px;
+}
+
+.w_dw_w_r_g_item>>>.el-radio__label{
+	font-size: 22px;
+}
+
+.w_dw_w_r_g_item>>>.el-radio__inner{
+	width: 20px;
+	height: 20px;
+	margin-right: 5px;
+}
+</style>

+ 252 - 0
src/components/pages/pblCourse/index.vue

@@ -0,0 +1,252 @@
+<template>
+	<div class="pblCourse" v-loading="loading">
+		<div class="pc_left">
+			<div class="pc_l_top">
+				<procedureArea :phase="phase" />
+			</div>
+			<div class="pc_l_bottom">
+				<doWorkArea :phase="phase" @changePhase="changePhase" @choiceAnswer="choiceAnswer" @submitTask="submitTask" :task="taskList[phase.atPhase]" @getTaskList="getTaskList"/>
+			</div>
+		</div>
+		<div class="pc_right">
+			<chatArea />
+		</div>
+		<selectTopicDialog ref="selectTopicDialogRef" @success="selectTopicSuccess"/>
+	</div>
+</template>
+
+<script>
+import chatArea from './component/chatArea'
+import doWorkArea from './component/doWorkArea'
+import procedureArea from './component/procedureArea'
+import { v4 as uuidv4 } from "uuid";
+import selectTopicDialog from './component/selectTopicDialog'
+export default {
+	components: {
+		chatArea,
+		doWorkArea,
+		procedureArea,
+		selectTopicDialog,
+	},
+	data() {
+		return {
+			loading: false,
+			phase:{
+				doPhase:0,
+				atPhase:0,
+			},
+			selectTopic:"",
+			taskList:[]
+		};
+	},
+	methods: {
+		changePhase(type,newValue){
+			this.phase[type] = newValue;
+		},
+		getTaskList(phase = 0){
+			return new Promise((resolve,reject)=>{
+				if(this.loading)return this.$message.info("请稍等")
+			this.loading = true;
+		const _uuid = uuidv4()
+		const _msg = `
+NOTICE
+Role: 作为学生的学习指导Agent,你熟悉熟悉PBL(基于问题的学习)和5EX教学模型,能够根据学生的学情数据(当前的学习任务设计、学习表现数据、作业数据等)生成自适应的学习任务和对应的5道考核题目。
+Language: Please use the same language as the user requirement, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
+ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
+Instruction: Based on the context, follow "Format example", write content.
+
+# Context
+## 语气
+你的语气应该是亲切地,有趣的,循循善诱的一个老师
+
+## 工具能力
+1. 5E教学模型应用:
+你需要熟悉并应用5E教学模型(即引入、探索、解释、扩展和评估)于学习任务的设计中,确保学习过程的有效性和吸引力。5E教学模型是一种以学生为中心的教学方法,旨在通过五个阶段(Engage, Explore, Explain, Elaborate, Evaluate)来促进学生的学习和理解。
+Engage(引入):在这个阶段,教师通过引人入胜的活动或问题来激发学生的兴趣和好奇心,帮助他们建立与新知识的联系。
+Explore(探索):学生通过动手实验或调查活动来探索新概念,培养他们的探究能力和批判性思维。
+Explain(解释):学生在这个阶段分享他们的发现,教师提供进一步的解释和指导,帮助学生理解新概念。
+Elaborate(拓展):学生通过应用新知识来解决更复杂的问题,进一步深化他们的理解。
+Evaluate(评估):教师和学生共同评估学习效果,反思学习过程,确定需要改进的地方。
+2. 学生表现与选择的感知:
+通过与学生互动,实时感知学生的学习表现和选择,理解他们的学习需求和难点。
+3. 自适应任务生成:
+基于学生的反馈和选择,自动生成个性化的学习任务,任务难度和类型随学生的表现和需求而变化。
+
+## 工作流程
+1. 判断学生当前处在5E模型中哪一个学习阶段。如果未提供学情数据,或无法判断学生当前处在5E教学模型中的哪一个解释,则默认处在第一个阶段(引入)阶段。请随机选择一个适合小学五年级学生的科学学习主题,并生成相应的符合引入阶段的学习任务。
+2. 结合学生当前的学情数据,生成紧随其后的下一个阶段的学习任务,但是仅仅生成紧随其后的下一个阶段的学习任务。你需要沿着这个顺序判断:引入阶段→探索阶段→解释阶段→拓展阶段→评估阶段。比如,当你判断学情数据中,学生目前已经完成了引入阶段的学习,那么你需要提供探索阶段的学习任务。
+3. 生成上一步中学习任务对应的5道考核选择题
+
+## 限制
+1. 请仅仅生成某一个阶段对应的学习任务。不要同时给出多个阶段、多个学习任务。
+2. 请严格按照以下格式要求输出内容,请仅仅告知相应的5E阶段名称和对应的任务描述,不需要包含学情数据等与【任务】无关的内容。
+3. 生成相应的考核题目,仅限单选题
+4. 任务描述的格式以markdown方式输出
+
+## 学情数据
+选题:${this.selectTopic}
+${this.phase.doPhase==0?'':`这是你生成适应性学习任务时,需要参考的前置学情数据${JSON.stringify(this.taskList[this.phase.doPhase-1])}(当前的学习任务设计、学习表现数据、作业数据等)。`}
+
+# Format example
+{
+"name": "任务名字",
+"detail": "任务描述(要求markdown的格式)",
+"target":"任务目标",
+"steps":"任务步骤",
+"tips":"任务提示",
+"answerArray":[
+{
+	"title": "标题",
+	"type": "单选题",
+	"option": ["选项1","选项2","选项3","选项4"],
+	"answer": "答案(最好是index)"
+},
+{
+	"title": "标题",
+	"type": "单选题",
+	"option": ["选项1","选项2","选项3","选项4"],
+	"answer": "答案(最好是index)"
+}
+]
+}
+
+Instruction: Based on the context, follow "Format example", write content.
+`
+			console.log(_msg)
+			// ${
+			// 	this.phase.doPhase==0?'':`
+			// 	## 学情数据
+			// 	这是你生成适应性学习任务时,需要参考的前置学情数据${JSON.stringify(this.taskList[this.phase.doPhase])}(当前的学习任务设计、学习表现数据、作业数据等)。`
+			// }
+
+			let params = {
+				model: "gpt-3.5-turbo",
+				temperature: 0,
+				max_tokens: 4096,
+				top_p: 1,
+				frequency_penalty: 0,
+				presence_penalty: 0,
+				messages: [{role:"user",content:_msg}],
+				uid: _uuid,
+				mind_map_question: "",
+				stream:false
+			}
+			this.ajax
+			.post("https://gpt4.cocorobo.cn/chat", params)
+			.then((res) => {
+				let _data = res.data.FunctionResponse.choices[0];
+				let content = _data.message.content;
+				console.log(content)
+				content = content.replaceAll('```json','').replaceAll('```','')
+				// console.log(content)
+				const _result = JSON.parse(content);
+				
+				_result.detail = _result.detail?_result.detail:"",
+				_result.steps = _result.steps?_result.steps:"",
+				_result.target = _result.target?_result.target:"",
+				_result.tips =_result.tips?_result.tips:""
+				this.taskList[phase] = _result;
+				// this.phase.doPhase = phase;
+				this.phase.atPhase = phase;
+				console.log(this.taskList)
+				this.loading = false;
+				resolve();
+			})
+			.catch((e) => {
+				this.loading = false;
+				this.$message.error("获取任务失败")
+				if(this.phase.doPhase!=0){
+					this.phase.doPhase--;
+				}
+				resolve();
+				console.log(e);
+			});
+			})
+			
+		},
+		choiceAnswer(_data){
+			this.taskList[this.phase.atPhase].answerArray[_data[0]].userAnswer = _data[1];
+			this.$forceUpdate()
+		},
+		submitTask(){
+			
+			this.loading = true;
+			let sum = 0;
+			this.taskList[this.phase.atPhase].answerArray.forEach(i=>{
+				if('userAnswer' in i){
+					sum++;
+				}
+			})
+			if(sum<this.taskList[this.phase.atPhase].answerArray.length){
+				this.loading = false;
+				return this.$message.error("当前阶段还未完成")
+			}else if((this.phase.doPhase>this.phase.atPhase)){
+				this.loading = false;
+				return this.$message.error("该阶段已经提交过了")
+			}else{
+				this.loading = false;
+				this.phase.doPhase++;
+				if(this.phase.doPhase==5){
+					return this.$message.error('所有阶段已经完成');
+				}
+				this.getTaskList(this.phase.doPhase)
+			}
+		},
+		selectTopicSuccess(_form){
+			if(_form.title){
+					this.phase = {
+					doPhase:0,
+					atPhase:0,
+				}
+				this.selectTopic = _form.title;
+				this.getTaskList()
+				this.$refs.selectTopicDialogRef.close();
+			}
+		}
+	},
+	mounted() {
+		
+		this.selectTopic = "";
+		this.$refs.selectTopicDialogRef.open();
+		// this.getTaskList()
+	},
+};
+</script>
+
+<style scoped>
+.pblCourse {
+	min-width: 1500px;
+	/* min-height: 800px; */
+	width: 100%;
+	height: 100vh;
+	display: flex;
+	background-color: #f0f2f5;
+	box-sizing: border-box;
+	padding: 20px;
+}
+
+.pc_left {
+	width: calc(100% - 500px - 20px);
+	margin-right: 20px;
+	box-sizing: border-box;
+}
+
+.pc_l_top {
+	width: 100%;
+	height: 150px;
+	box-sizing: border-box;
+}
+
+.pc_l_bottom {
+	width: 100%;
+	height: calc(100% - 150px - 15px);
+	box-sizing: border-box;
+	margin-top: 15px;
+}
+
+.pc_right {
+	width: 500px;
+	height: 100%;
+	box-sizing: border-box;
+}
+</style>

+ 0 - 2
src/components/pages/sz/teacher.vue

@@ -157,7 +157,6 @@
       :append-to-body="true"
       width="700px"
       :before-close="handleClose"
-      :close-on-click-modal="true"
       class="add_student"
     >
       <el-form class="inputClass">
@@ -227,7 +226,6 @@
       :append-to-body="true"
       width="700px"
       :before-close="handleClose"
-      :close-on-click-modal="true"
       class="add_student"
     >
       <!-- <div slot="title" class="header-title">

+ 9 - 0
src/router/index.js

@@ -125,6 +125,7 @@ import studentEva from '@/components/pages/studentEva'
 import kindStudentEva from '@/components/pages/kindStudentEva/index'
 import record from '@/components/pages/record/class'
 import classroomObservation from '@/components/pages/classroomObservation/index'//课堂观察
+import pblCourse from '@/components/pages/pblCourse/index'
 
 // 全局修改默认配置,点击空白处不能关闭弹窗
 ElementUI.Dialog.props.closeOnClickModal.default = false
@@ -1073,6 +1074,14 @@ export default new Router({
 					meta:{
 						requireAuth:''//不需要鉴权
 					}
+				},
+				{//pblCourse
+					path:"/pblCourse",
+					name:"pblCourse",
+					component:pblCourse,
+					meta:{
+						requireAuth:''//不需要鉴权
+					}
 				}
     ]
 })

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است