Browse Source

语音助手

SanHQin 8 months ago
parent
commit
909302f36a

BIN
src/assets/icon/course/alarmClock.png


+ 76 - 27
src/components/classRoomHelper/component/dialogArea.vue

@@ -29,15 +29,21 @@
                   class="d_t_c_a_r_content"
                   v-loading="item.loading"
                   v-html="item.aiContent"
+									
                 ></div>
 
-                <div v-else v-for="i in pan(item.aiContent)">
+                <div v-else v-for="i in pan(item.aiContent)"  style="position: relative;" class="d_t_c_a_r_c_img">
                   <img
                     style="width: 130px;height: 130px;object-fit: cover;"
                     :src="i.image"
                     alt=""
                     @click="previewImg(i.image)"
                   />
+									<span class="download_image" @click.stop="download(i.image)">
+                  <img
+                    :src="require('../../../assets/icon/fileIcon/download.png')"
+                  />
+                </span>
                 </div>
               </div>
               <div
@@ -438,34 +444,34 @@ export default {
         });
         history.push({ role: "user", content: _text });
         // history.pop();
-        // let params = {
-        //   model: "gpt-3.5-turbo",
-        //   temperature: 0,
-        //   max_tokens: 4096,
-        //   top_p: 1,
-        //   frequency_penalty: 0,
-        //   presence_penalty: 0,
-        //   messages: history,
-        //   uid: _uuid,
-        //   mind_map_question: _text
-        // };
         let params = {
-          message: {
-            anthropic_version: "bedrock-2023-05-31",
-            max_tokens: 4096,
-            temperature: 0,
-            top_p: 1,
-            messages: history
-          },
+          model: "gpt-3.5-turbo",
+          temperature: 0,
+          max_tokens: 4096,
+          top_p: 1,
+          frequency_penalty: 0,
+          presence_penalty: 0,
+          messages: history,
           uid: _uuid,
-          model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+          mind_map_question: _text
         };
+        // let params = {
+        //   message: {
+        //     anthropic_version: "bedrock-2023-05-31",
+        //     max_tokens: 4096,
+        //     temperature: 0,
+        //     top_p: 1,
+        //     messages: history
+        //   },
+        //   uid: _uuid,
+        //   model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+        // };
         this.text = "";
         console.log("发送信息", params);
 
         this.ajax
-          .post("https://claude3.cocorobo.cn/chat", params)
-          // .post("https://gpt4.cocorobo.cn/chat", params)
+          // .post("https://claude3.cocorobo.cn/chat", params)
+          .post("https://gpt4.cocorobo.cn/chat", params)
           .then(res => {
             if (res.data.FunctionResponse.result == "发送成功") {
             } else {
@@ -483,8 +489,8 @@ export default {
     },
     // 获取ai对话
     getAiContent(_uid) {
-			this.source = new EventSource(`https://claude3.cocorobo.cn/streamChat/${_uid}`);
-      // this.source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/     https://gpt4.cocorobo.cn/stream/
+			// this.source = new EventSource(`https://claude3.cocorobo.cn/streamChat/${_uid}`);
+      this.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();
@@ -527,9 +533,8 @@ export default {
       };
     },
     getAtAuContent(_uid) {
-      this.source = new EventSource(
-        `https://gpt4.cocorobo.cn/question/${_uid}`
-      ); //http://gpt4.cocorobo.cn:8011/question/   https://gpt4.cocorobo.cn/question/
+      this.source = new EventSource(`https://gpt4.cocorobo.cn/question/${_uid}`); 
+			//http://gpt4.cocorobo.cn:8011/question/   https://gpt4.cocorobo.cn/question/
       let _allText = "";
       let _mdText = "";
       // const md = new MarkdownIt();
@@ -1024,6 +1029,31 @@ ${_text}
       this.$nextTick(() => {
         this.$refs.chatRef.scrollTop = this.$refs.chatRef.scrollHeight;
       });
+    },
+		download(_url) {
+			let xhr = new XMLHttpRequest();
+        xhr.open("GET", _url, true);
+        xhr.responseType = "blob";
+					xhr.onload = () => {
+          if (xhr.status === 200) {
+            let blob = xhr.response;
+            // const _blob = new Blob([blob], { type: fileType });
+            const downloadElement = document.createElement("a");
+            const url = window.URL.createObjectURL(blob);
+            downloadElement.href = url;
+            downloadElement.download = "Image.jpg";
+            downloadElement.click();
+            window.URL.revokeObjectURL(url); // 释放内存
+          }else{
+						this.$message.error("此图片不支持下载");
+					}
+        };
+				xhr.onerror = (e)=>{
+					console.log(e)
+					this.$message.error("此图片不支持下载");
+				}
+        
+        xhr.send();
     }
   },
   mounted() {
@@ -1511,4 +1541,23 @@ ${_text}
 .s_t_addAsk>span:hover{
 	background-color:#E8E9EC;
 }
+
+.d_t_c_a_r_c_img:hover .download_image {
+  display: block;
+}
+
+.download_image {
+  position: absolute;
+  display: none;
+  right: 5px;
+  bottom: 5px;
+  width: 30px;
+  height: 30px;
+  cursor: pointer;
+}
+
+.download_image > img {
+  width: 100%;
+  height: 100%;
+}
 </style>

+ 169 - 61
src/components/classRoomHelper/component/levitatedSphere.vue

@@ -72,64 +72,153 @@ export default {
     }
   },
   methods: {
-    recordStart() {
+    recordStart(_text) {
       try {
-				let iiframe = this.$refs["iiframe"];
-      iiframe.contentWindow.window.document.getElementById(
-        "languageOptions"
-      ).selectedIndex = 2; //普通话
-      iiframe.contentWindow.testdoContinuousPronunciationAssessment();
-      iiframe.contentWindow.onRecognizedResult = e => {
-        let _msg = e.privText;
-        // let _msg = _text;
-        // console.log("👇");
-        // console.log(_msg);
-        if (_msg.indexOf("可可同学") != -1 && !this.show) {
-          this.aiText = "您好,我是小可,有什么可以帮助您的?";
-          this.aiStatus = 0;
-          this.showIndex = 0;
-          this.show = true;
-          console.log("已唤醒可可同学");
-        } else if (this.show == true) {
-          if (this.showTextIndex == 2) {
-            console.log("ai在组织语言");
-          } else {
-            this.showTextIndex = 1;
-            this.aiText = "";
-            this.userText += _msg;
-            this.aiStatus = 1;
-            if (this.timer) {
-              clearTimeout(this.timer);
-              this.timer = null;
-            }
-            this.timer = setTimeout(() => {
-              this.showTextIndex = 2;
+        let iiframe = this.$refs["iiframe"];
+        iiframe.contentWindow.window.document.getElementById(
+          "languageOptions"
+        ).selectedIndex = 2; //普通话
+        iiframe.contentWindow.testdoContinuousPronunciationAssessment();
+        iiframe.contentWindow.onRecognizedResult = e => {
+          let _msg = e.privText;
+          // let _msg = _text;
+          // console.log("👇");
+          // console.log(_msg);
+          if (_msg.indexOf("可可同学") != -1 && !this.show) {
+            this.aiText = "您好,我是小可,有什么可以帮助您的?";
+            this.aiStatus = 0;
+            this.showIndex = 0;
+            this.show = true;
+            console.log("已唤醒");
+          } else if (this.show == true) {
+            if (this.showTextIndex == 2) {
+              console.log("组织语言中");
+            } else {
+              this.showTextIndex = 1;
               this.aiText = "";
-              console.log("开始思考");
-              setTimeout(() => {
-                this.aiStatus = 0;
-                this.showTextIndex = 0;
-                this.aiText = "不知道,请您稍后再问...。";
-                this.timer = setTimeout(() => {
-                  this.showTextIndex = 3;
-                  this.aiStatus = 2;
-                  this.aiText = "";
-                  this.userText = "";
-                }, 5000);
-              }, 3000);
-            }, 5000);
+              this.userText += _msg;
+              this.aiStatus = 1;
+              if (this.timer) {
+                clearTimeout(this.timer);
+                this.timer = null;
+              }
+              this.timer = setTimeout(() => {
+								if(this.userText.indexOf("关闭语音助手") != -1){
+									return setTimeout(()=>{
+										this.show = false,
+										this.showTextIndex = 3;
+                    this.aiStatus = 2;
+                    this.aiText = "";
+                    this.userText = "";
+									},2000)
+								}
+                this.showTextIndex = 2;
+                this.aiText = "";
+                if (/计时(.+)分钟/.test(this.userText) != -1) {
+                  setTimeout(() => {
+										let _number = this.userText.match(/计时(.+)分钟/)[1]
+										let _time = 0;
+										if(!/^\d+$/.test(_number)){
+											console.log("👇")
+											console.log(_number)
+											_time = this.chineseToNumber(_number)*60;
+											console.log(this.chineseToNumber(_number))
+										}else{
+											_time = parseInt(_numberList[1])*60
+										}
+                    this.$emit("startTime", _time);
+										this.aiStatus = 0;
+										this.showTextIndex = 0;
+										this.aiText = "好的,我已为您计时"+this.userText.match(/计时(.+)分钟/)[1]+"分钟。";
+										this.userText = "";
+										this.timer = setTimeout(() => {
+											this.showTextIndex = 3;
+											this.aiStatus = 2;
+											this.aiText = "";
+											this.userText = "";
+										}, 2000);
+                  }, 2000);
+								} else {
+                  setTimeout(() => {
+                    this.aiStatus = 0;
+                    this.showTextIndex = 0;
+                    this.aiText = "不知道,请您稍后再问...。";
+                    this.timer = setTimeout(() => {
+                      this.showTextIndex = 3;
+                      this.aiStatus = 2;
+                      this.aiText = "";
+                      this.userText = "";
+                    }, 5000);
+                  }, 3000);
+                }
+              }, 5000);
+            }
+          } else {
+            console.log("不响应");
+          }
+        };
+      } catch (error) {
+        console.log("ai录音报错👇");
+        console.log(error);
+        setTimeout(() => {
+          this.recordStart();
+        }, 1000);
+      }
+    },
+    chineseToNumber(chinese) {
+      const chineseNumbers = {
+        零: 0,
+        一: 1,
+        二: 2,
+        三: 3,
+        四: 4,
+        五: 5,
+        六: 6,
+        七: 7,
+        八: 8,
+        九: 9,
+        十: 10,
+        百: 100,
+        千: 1000,
+        万: 10000,
+        亿: 100000000
+      };
+
+      let result = 0;
+      let tempNum = 0; // 用于累积处理
+      let sectionNum = 0; // 每个段的值
+
+      for (let i = 0; i < chinese.length; i++) {
+        const char = chinese[i];
+        const num = chineseNumbers[char];
+
+        if (num === undefined) {
+          throw new Error(`Unexpected character: ${char}`);
+        }
+
+        if (
+          num === 10 ||
+          num === 100 ||
+          num === 1000 ||
+          num === 10000 ||
+          num === 100000000
+        ) {
+          if (tempNum === 0) tempNum = 1; // 如果前面没有数,默认是1
+          tempNum *= num;
+
+          if (num === 10000 || num === 100000000) {
+            sectionNum += tempNum;
+            result += sectionNum;
+            tempNum = 0;
+            sectionNum = 0;
           }
         } else {
-          console.log("不响应");
+          tempNum += num;
         }
-      };
-			} catch (error) {
-				console.log("ai录音报错👇")
-				console.log(error)
-				setTimeout(()=>{
-					this.recordStart();
-				},1000)
-			}
+      }
+
+      result += sectionNum + tempNum;
+      return result;
     }
   },
   mounted() {
@@ -140,14 +229,34 @@ export default {
     //     this.recordStart("可可同学。");
     //     setTimeout(() => {
     //       setTimeout(() => {
-    //         this.recordStart("你是谁呀。");
+    //         this.recordStart("您好可可,");
     //         setTimeout(() => {
-    //           this.recordStart("平时都在做什么?");
-    //         }, 2000);
-    //       }, 3000);
-    //     }, 6000);
-    //   }, 3000);
-    // },3000);
+    //           this.recordStart("帮我计时一分钟的闹钟");
+		// 					setTimeout(()=>{
+		// 						this.recordStart("您好可可,关闭语音助手");
+		// 						setTimeout(() => {
+    // 						  this.recordStart("嘿哟黑");
+    // 						  setTimeout(() => {
+    // 						    this.recordStart("可可同学。");
+    // 						    setTimeout(() => {
+    // 						      setTimeout(() => {
+    // 						        this.recordStart("您好可可,");
+    // 						        setTimeout(() => {
+    // 						          this.recordStart("帮我计时一分钟的闹钟");
+		// 											setTimeout(()=>{
+		// 												this.recordStart("您好可可,关闭语音助手");
+		// 											},20000)
+    // 						        }, 1000);
+    // 						      }, 1000);
+    // 						    }, 1000);
+    // 						  }, 1000);
+    // 						},20000);
+		// 					},20000)
+    //         }, 1000);
+    //       }, 1000);
+    //     }, 1000);
+    //   }, 1000);
+    // },1000);
   }
 };
 </script>
@@ -161,7 +270,6 @@ export default {
   right: 10px;
   z-index: 9999;
   display: flex;
-  transform: rotate(0);
 }
 
 .ls_hello {

+ 169 - 96
src/components/classRoomHelper/component/searchArea.vue

@@ -70,6 +70,8 @@
                 v-else
                 v-for="(i, index) in pan(item.aiContent)"
                 :key="index"
+								style="position: relative;"
+                class="d_t_c_a_r_c_img"
               >
                 <img
                   style="width: 130px;height: 130px;object-fit: cover;"
@@ -77,6 +79,11 @@
                   alt=""
                   @click="previewImg(i.image)"
                 />
+								<span class="download_image" @click.stop="download(i.image)">
+                  <img
+                    :src="require('../../../assets/icon/fileIcon/download.png')"
+                  />
+                </span>
               </div>
               <!-- {{ item }} -->
               <div style="margin-top: 10px;width: 100%;display: flex;justify-content: end;" v-if="pan(item.aiContent).length > 0 && chatList.length-2 == index" >
@@ -936,34 +943,34 @@ Instruction: Based on the context, follow "Format example", write content.
       }else{
         history.push({ role: "user", content: _text });
       }
-      // let params = {
-      //   model: "gpt-3.5-turbo",
-      //   temperature: 0,
-      //   max_tokens: 4096,
-      //   top_p: 1,
-      //   frequency_penalty: 0,
-      //   presence_penalty: 0,
-      //   messages: history,
-      //   uid: _uuid,
-      //   mind_map_question: _text
-      // };
       let params = {
-        message: {
-          anthropic_version: "bedrock-2023-05-31",
-          max_tokens: 4096,
-          temperature: 0,
-          top_p: 1,
-          messages: history
-        },
+        model: "gpt-3.5-turbo",
+        temperature: 0,
+        max_tokens: 4096,
+        top_p: 1,
+        frequency_penalty: 0,
+        presence_penalty: 0,
+        messages: history,
         uid: _uuid,
-        model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+        mind_map_question: _text
       };
+      // let params = {
+      //   message: {
+      //     anthropic_version: "bedrock-2023-05-31",
+      //     max_tokens: 4096,
+      //     temperature: 0,
+      //     top_p: 1,
+      //     messages: history
+      //   },
+      //   uid: _uuid,
+      //   model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+      // };
       this.text = "";
       // console.log('56465166541561616',params);
 
       this.ajax
-        .post("https://claude3.cocorobo.cn/chat", params)
-        // .post("https://gpt4.cocorobo.cn/chat", params)
+        // .post("https://claude3.cocorobo.cn/chat", params)
+        .post("https://gpt4.cocorobo.cn/chat", params)
         .then(res => {
           if (res.data.FunctionResponse.result == "发送成功") {
           } else {
@@ -1093,34 +1100,34 @@ ${_atList
       // if (_msg) {
       history.push({ role: "user", content: _msg });
       // }
-      // history.push({ role: "user", content: _text });
-      // let params = {
-      //   model: "gpt-3.5-turbo",
-      //   temperature: 0,
-      //   max_tokens: 4096,
-      //   top_p: 1,
-      //   frequency_penalty: 0,
-      //   presence_penalty: 0,
-      //   messages: history,
-      //   uid: _uuid,
-      //   mind_map_question: noAtText
-      // };
+      history.push({ role: "user", content: _text });
       let params = {
-        message: {
-          anthropic_version: "bedrock-2023-05-31",
-          max_tokens: 4096,
-          temperature: 0,
-          top_p: 1,
-          messages: history
-        },
+        model: "gpt-3.5-turbo",
+        temperature: 0,
+        max_tokens: 4096,
+        top_p: 1,
+        frequency_penalty: 0,
+        presence_penalty: 0,
+        messages: history,
         uid: _uuid,
-        model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+        mind_map_question: noAtText
       };
+      // let params = {
+      //   message: {
+      //     anthropic_version: "bedrock-2023-05-31",
+      //     max_tokens: 4096,
+      //     temperature: 0,
+      //     top_p: 1,
+      //     messages: history
+      //   },
+      //   uid: _uuid,
+      //   model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+      // };
       this.text = "";
 
       this.ajax
-        // .post("https://gpt4.cocorobo.cn/chat", params)
-        .post("https://claude3.cocorobo.cn/chat", params)
+        .post("https://gpt4.cocorobo.cn/chat", params)
+        // .post("https://claude3.cocorobo.cn/chat", params)
         .then(res => {
           if (res.data.FunctionResponse.result == "发送成功") {
           } else {
@@ -1173,10 +1180,10 @@ ${_atList
     },
     // 获取ai对话
     getAiContent(_uid) {
-      this.source = new EventSource(
-        `https://claude3.cocorobo.cn/streamChat/${_uid}`
-      );
-      // this.source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/     https://gpt4.cocorobo.cn/stream/
+      // this.source = new EventSource(
+      //   `https://claude3.cocorobo.cn/streamChat/${_uid}`
+      // );
+      this.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();
@@ -1220,10 +1227,10 @@ ${_atList
       };
     },
     getWAntSearchContent(_uid) {
-      this.source = new EventSource(
-        `https://claude3.cocorobo.cn/streamChat/${_uid}`
-      );
-      // this.source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/     https://gpt4.cocorobo.cn/stream/
+      // this.source = new EventSource(
+      //   `https://claude3.cocorobo.cn/streamChat/${_uid}`
+      // );
+      this.source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/     https://gpt4.cocorobo.cn/stream/
       let _allText = "";
       let _mdText = "";
       this.scrollBottom();
@@ -1423,33 +1430,33 @@ ${_atList
 
       this.scrollBottom();
 
-      // 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: ""
-      // };
       let params = {
-        message: {
-          anthropic_version: "bedrock-2023-05-31",
-          max_tokens: 4096,
-          temperature: 0,
-          top_p: 1,
-          messages: [{ role: "user", content: _msg }]
-        },
+        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,
-        model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+        mind_map_question: ""
       };
+      // let params = {
+      //   message: {
+      //     anthropic_version: "bedrock-2023-05-31",
+      //     max_tokens: 4096,
+      //     temperature: 0,
+      //     top_p: 1,
+      //     messages: [{ role: "user", content: _msg }]
+      //   },
+      //   uid: _uuid,
+      //   model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+      // };
       this.text = "";
 
       this.ajax
-        // .post("https://gpt4.cocorobo.cn/chat", params)
-        .post("https://claude3.cocorobo.cn/chat", params)
+        .post("https://gpt4.cocorobo.cn/chat", params)
+        // .post("https://claude3.cocorobo.cn/chat", params)
         .then(res => {
           if (res.data.FunctionResponse.result == "发送成功") {
           } else {
@@ -1818,20 +1825,31 @@ ${_wordData}
 
           // if (_msg) {
           history.push({ role: "user", content: _msg });
-          let params = {
-            message: {
-              anthropic_version: "bedrock-2023-05-31",
-              max_tokens: 4096,
-              temperature: 0,
-              top_p: 1,
-              messages: history
-            },
-            uid: _uuid,
-            model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
-          };
+					let params = {
+        	  model: "gpt-3.5-turbo",
+        	  temperature: 0,
+        	  max_tokens: 4096,
+        	  top_p: 1,
+        	  frequency_penalty: 0,
+        	  presence_penalty: 0,
+        	  messages: history,
+        	  uid: _uuid,
+        	  mind_map_question: _text
+        	};
+          // let params = {
+          //   message: {
+          //     anthropic_version: "bedrock-2023-05-31",
+          //     max_tokens: 4096,
+          //     temperature: 0,
+          //     top_p: 1,
+          //     messages: history
+          //   },
+          //   uid: _uuid,
+          //   model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+          // };
           this.ajax
-            // .post("https://gpt4.cocorobo.cn/chat", params)
-            .post("https://claude3.cocorobo.cn/chat", params)
+            .post("https://gpt4.cocorobo.cn/chat", params)
+            // .post("https://claude3.cocorobo.cn/chat", params)
             .then(res => {
               if (res.data.FunctionResponse.result == "发送成功") {
                 this.loading = false;
@@ -1990,20 +2008,31 @@ ${_wordData}
 
       // if (_msg) {
       history.push({ role: "user", content: _msg });
-      let params = {
-        message: {
-          anthropic_version: "bedrock-2023-05-31",
-          max_tokens: 4096,
+			let params = {
+          model: "gpt-3.5-turbo",
           temperature: 0,
+          max_tokens: 4096,
           top_p: 1,
-          messages: history
-        },
-        uid: _uuid,
-        model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
-      };
+          frequency_penalty: 0,
+          presence_penalty: 0,
+          messages: history,
+          uid: _uuid,
+          mind_map_question: _text
+        };
+      // let params = {
+      //   message: {
+      //     anthropic_version: "bedrock-2023-05-31",
+      //     max_tokens: 4096,
+      //     temperature: 0,
+      //     top_p: 1,
+      //     messages: history
+      //   },
+      //   uid: _uuid,
+      //   model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
+      // };
       this.ajax
-        // .post("https://gpt4.cocorobo.cn/chat", params)
-        .post("https://claude3.cocorobo.cn/chat", params)
+        .post("https://gpt4.cocorobo.cn/chat", params)
+        // .post("https://claude3.cocorobo.cn/chat", params)
         .then(res => {
           if (res.data.FunctionResponse.result == "发送成功") {
           } else {
@@ -2017,6 +2046,31 @@ ${_wordData}
         });
       this.saveUid = _uuid;
       this.getAiContent(_uuid);
+    },
+		download(_url) {
+			let xhr = new XMLHttpRequest();
+        xhr.open("GET", _url, true);
+        xhr.responseType = "blob";
+					xhr.onload = () => {
+          if (xhr.status === 200) {
+            let blob = xhr.response;
+            // const _blob = new Blob([blob], { type: fileType });
+            const downloadElement = document.createElement("a");
+            const url = window.URL.createObjectURL(blob);
+            downloadElement.href = url;
+            downloadElement.download = "Image.jpg";
+            downloadElement.click();
+            window.URL.revokeObjectURL(url); // 释放内存
+          }else{
+						this.$message.error("此图片不支持下载");
+					}
+        };
+				xhr.onerror = (e)=>{
+					console.log(e)
+					this.$message.error("此图片不支持下载");
+				}
+        
+        xhr.send();
     }
   },
   mounted() {
@@ -2733,4 +2787,23 @@ ${_wordData}
 .s_t_addAsk>span:hover{
 	background-color:#E8E9EC;
 }
+
+.d_t_c_a_r_c_img:hover .download_image {
+  display: block;
+}
+
+.download_image {
+  position: absolute;
+  display: none;
+  right: 5px;
+  bottom: 5px;
+  width: 30px;
+  height: 30px;
+  cursor: pointer;
+}
+
+.download_image > img {
+  width: 100%;
+  height: 100%;
+}
 </style>

+ 356 - 269
src/components/classRoomHelper/component/taskArea.vue

@@ -7,14 +7,24 @@
           <span>课程时间轴</span>
         </div>
         <div class="tt_i_box">
-					<div class="tt_i_b_step" v-if="navList.length==1">
-						<!-- 只有一个阶段 -->
-						<span :style="{'background':taskCount>=index?'#3681FC':'#E0EAFB'}" v-for="(item,index) in navList[courseType].task"></span>
-					</div>
-					<div  class="tt_i_b_step" v-if="navList.length>1">
-						<!-- 多个阶段 -->
-						<span :style="{'background':courseType>=index?'#3681FC':'#E0EAFB'}" v-for="(item,index) in navList"></span>
-					</div>
+          <div class="tt_i_b_step" v-if="navList.length == 1">
+            <!-- 只有一个阶段 -->
+            <span
+              :style="{
+                background: taskCount >= index ? '#3681FC' : '#E0EAFB'
+              }"
+              v-for="(item, index) in navList[courseType].task"
+            ></span>
+          </div>
+          <div class="tt_i_b_step" v-if="navList.length > 1">
+            <!-- 多个阶段 -->
+            <span
+              :style="{
+                background: courseType >= index ? '#3681FC' : '#E0EAFB'
+              }"
+              v-for="(item, index) in navList"
+            ></span>
+          </div>
           <!-- <img :src="require('../../../assets/icon/course/group.png')" /> -->
         </div>
       </div>
@@ -32,7 +42,14 @@
                 <div>提交人数</div>
                 <span>{{ isWorkStudent }}<span>人</span></span>
               </div>
-              <div class="tt_i_b_b_item" v-if="(this.$route.query.tcid || courseDetail.juri) && allStudent && complete!='-'">
+              <div
+                class="tt_i_b_b_item"
+                v-if="
+                  (this.$route.query.tcid || courseDetail.juri) &&
+                    allStudent &&
+                    complete != '-'
+                "
+              >
                 <div>完成率</div>
                 <span>{{ complete }}<span>%</span></span>
               </div>
@@ -47,11 +64,11 @@
               </div>
               <div class="tt_i_b_b_item">
                 <div>点赞数</div>
-                <span>{{likeNum}}<span>个</span></span>
+                <span>{{ likeNum }}<span>个</span></span>
               </div>
               <div class="tt_i_b_b_item">
                 <div>评论数</div>
-                <span>{{commentNum}}<span>条</span></span>
+                <span>{{ commentNum }}<span>条</span></span>
               </div>
             </div>
           </div>
@@ -60,7 +77,7 @@
     </div>
     <div class="t_top" ref="chatRef">
       <div class="t_t_chat" v-for="(item, index) in chatList" :key="index">
-        <div class="t_t_c_user" v-if="item.content && item.content!='addAsk'">
+        <div class="t_t_c_user" v-if="item.content && item.content != 'addAsk'">
           <div class="t_t_c_u_left">
             <div class="t_t_c_u_l_content">{{ item.content }}</div>
             <div class="t_t_c_u_l_time">{{ item.createtime }}</div>
@@ -69,7 +86,7 @@
             <span>我</span>
           </div>
         </div>
-        <div class="t_t_c_ai" v-if="item.content!='addAsk'">
+        <div class="t_t_c_ai" v-if="item.content != 'addAsk'">
           <div class="t_t_c_a_left">
             <el-avatar v-if="item.filename" :src="item.filename"></el-avatar>
             <span v-else>Ai</span>
@@ -86,13 +103,23 @@
                 v-loading="item.loading"
                 v-html="item.aiContent"
               ></div>
-              <div v-else v-for="i in pan(item.aiContent)">
+              <div
+                v-else
+                v-for="i in pan(item.aiContent)"
+                style="position: relative;"
+                class="d_t_c_a_r_c_img"
+              >
                 <img
                   style="width: 130px;height: 130px;object-fit: cover;"
                   :src="i.image"
                   alt=""
                   @click="previewImg(i.image)"
                 />
+                <span class="download_image" @click.stop="download(i.image)">
+                  <img
+                    :src="require('../../../assets/icon/fileIcon/download.png')"
+                  />
+                </span>
               </div>
             </div>
             <div
@@ -112,9 +139,14 @@
             <div class="t_t_c_a_r_time">{{ item.createtime }}</div>
           </div>
         </div>
-				<div class="s_t_addAsk" v-if="item.content == 'addAsk'">
-					<span v-for="item2 in item.aiContent" :key="item2.index" @click.stop="send(item2.label)">{{item2.label}}</span>
-				</div>
+        <div class="s_t_addAsk" v-if="item.content == 'addAsk'">
+          <span
+            v-for="item2 in item.aiContent"
+            :key="item2.index"
+            @click.stop="send(item2.label)"
+            >{{ item2.label }}</span
+          >
+        </div>
       </div>
     </div>
     <div class="t_bottom">
@@ -133,7 +165,7 @@
             :disabled="loading || chatLoading"
             v-loading="loading || chatLoading"
             @keyup.enter.native="send()"
-						placeholder="请在此输入您想了解的内容"
+            placeholder="请在此输入您想了解的内容"
             class="t_b_i_left"
             v-model="text"
           ></el-input>
@@ -141,10 +173,10 @@
 						<span></span>
 					</div> -->
         </div>
-				<div class="t_b_btn" @click="send()">
-            <span v-if="(!loading && !chatLoading)"></span>
-						<div v-else @click.stop="stopSend()">停止</div>
-          </div>
+        <div class="t_b_btn" @click="send()">
+          <span v-if="!loading && !chatLoading"></span>
+          <div v-else @click.stop="stopSend()">停止</div>
+        </div>
       </div>
     </div>
   </div>
@@ -161,22 +193,22 @@ export default {
       type: Object,
       default: () => {}
     },
-		navList:{
-			type:Array,
-			default:()=>[]
-		},
-		courseType:{
-			type:Number,
-			default:0
-		},
-		taskCount:{
-			type:Number,
-			default:0
-		},
-		worksStudent:{
-			type:Array,
-			default:()=>[]
-		}
+    navList: {
+      type: Array,
+      default: () => []
+    },
+    courseType: {
+      type: Number,
+      default: 0
+    },
+    taskCount: {
+      type: Number,
+      default: 0
+    },
+    worksStudent: {
+      type: Array,
+      default: () => []
+    }
   },
   data() {
     return {
@@ -187,94 +219,102 @@ export default {
       chatLoading: false,
       chatList: [],
       nowChatList: [],
-			source:null,
-			saveUid:"",
-			allStudent:[],
-			tcid:this.$route.query.tcid,
-			oid:this.$route.query.oid,
+      source: null,
+      saveUid: "",
+      allStudent: [],
+      tcid: this.$route.query.tcid,
+      oid: this.$route.query.oid
     };
   },
   computed: {
-		isWorkAll(){
-			let _userList = []
-			if(this.worksStudent.length){
-				this.worksStudent.forEach(i=>{
-					if(i.length){
-						i.forEach(i2=>{
-							if(i2.commentJson){
-								i2.commentJson.forEach(i3=>{
-								if(i3.userid && !_userList.find(i5=>i5.userid==i3.userid)){
-									_userList.push(i3)
-								}
-							})
-							}
-							
-							if(i2.likeJson){
-								i2.likeJson.forEach(i4=>{
-								if(i4.likesId && !_userList.find(i5=>i5.userid==i4.likesId)){
-									_userList.push({...i4,userid:i4.likesId})
-								}
-							})
-							}
-							
-						})
-					}
-				})
-			}
-			return _userList.length;
-		},
-		complete(){
-			let _result = 0;
-			let _all = this.allStudent?this.allStudent.length:0
-			if(_all==0)return '-'
-			_result = ((this.isWorkStudent/_all).toFixed(2))*100
-			return _result;
-		},
-		likeNum(){
-			let _result = 0;
-			if(this.worksStudent.length){
-				this.worksStudent.forEach(i=>{
-					if(i.length){
-						i.forEach(i2=>{
-							if(i2.likesCount){
-								_result+=i2.likesCount
-							}
-						})
-					}
-				})
-			}
-			return _result;
-		},
-		commentNum(){
-			let _result = 0;
-			if(this.worksStudent.length){
-				this.worksStudent.forEach(i=>{
-					if(i.length){
-						i.forEach(i2=>{
-							if(i2.commentCount){
-								_result+=i2.commentCount
-							}
-						})
-					}
-				})
-			}
-			return _result;
-		},
-		isWorkStudent(){
-			let _userList = []
-			if(this.worksStudent.length){
-				this.worksStudent.forEach(i=>{
-					if(i.length){
-						i.forEach(i2=>{
-							if(i2.ttype==2 && !_userList.find(i3=>i3.userid==i2.userid)){
-								_userList.push(i2)
-							}
-						})
-					}
-				})
-			}
-			return _userList.length;
-		},
+    isWorkAll() {
+      let _userList = [];
+      if (this.worksStudent.length) {
+        this.worksStudent.forEach(i => {
+          if (i.length) {
+            i.forEach(i2 => {
+              if (i2.commentJson) {
+                i2.commentJson.forEach(i3 => {
+                  if (
+                    i3.userid &&
+                    !_userList.find(i5 => i5.userid == i3.userid)
+                  ) {
+                    _userList.push(i3);
+                  }
+                });
+              }
+
+              if (i2.likeJson) {
+                i2.likeJson.forEach(i4 => {
+                  if (
+                    i4.likesId &&
+                    !_userList.find(i5 => i5.userid == i4.likesId)
+                  ) {
+                    _userList.push({ ...i4, userid: i4.likesId });
+                  }
+                });
+              }
+            });
+          }
+        });
+      }
+      return _userList.length;
+    },
+    complete() {
+      let _result = 0;
+      let _all = this.allStudent ? this.allStudent.length : 0;
+      if (_all == 0) return "-";
+      _result = ((this.isWorkStudent / _all) * 100).toFixed(2);
+      return _result;
+    },
+    likeNum() {
+      let _result = 0;
+      if (this.worksStudent.length) {
+        this.worksStudent.forEach(i => {
+          if (i.length) {
+            i.forEach(i2 => {
+              if (i2.likesCount) {
+                _result += i2.likesCount;
+              }
+            });
+          }
+        });
+      }
+      return _result;
+    },
+    commentNum() {
+      let _result = 0;
+      if (this.worksStudent.length) {
+        this.worksStudent.forEach(i => {
+          if (i.length) {
+            i.forEach(i2 => {
+              if (i2.commentCount) {
+                _result += i2.commentCount;
+              }
+            });
+          }
+        });
+      }
+      return _result;
+    },
+    isWorkStudent() {
+      let _userList = [];
+      if (this.worksStudent.length) {
+        this.worksStudent.forEach(i => {
+          if (i.length) {
+            i.forEach(i2 => {
+              if (
+                i2.ttype == 2 &&
+                !_userList.find(i3 => i3.userid == i2.userid)
+              ) {
+                _userList.push(i2);
+              }
+            });
+          }
+        });
+      }
+      return _userList.length;
+    },
     pan() {
       return content => {
         try {
@@ -292,19 +332,18 @@ export default {
     }
   },
   methods: {
-		stopSend(){
-			if(this.source){
-				this.source.close();
-				if(this.chatList[this.chatList.length-1].content=='wanSearch'){
-					this.chatList.pop();
-				}
-				this.loading = false;
-				this.chatLoading = false;
-				this.source = null;
-				this.insertChat(this.saveUid)
-				
-			}
-		},
+    stopSend() {
+      if (this.source) {
+        this.source.close();
+        if (this.chatList[this.chatList.length - 1].content == "wanSearch") {
+          this.chatList.pop();
+        }
+        this.loading = false;
+        this.chatLoading = false;
+        this.source = null;
+        this.insertChat(this.saveUid);
+      }
+    },
     onCopy(content) {
       // 创建临时textarea元素
       const tempInput = document.createElement("textarea");
@@ -369,8 +408,10 @@ export default {
     },
     // 获取ai对话
     getAiContent(_uid) {
-			this.source = new EventSource(`https://claude3.cocorobo.cn/streamChat/${_uid}`);
-      // this.source = new EventSource(`https://gpt4.cocorobo.cn/stream/${_uid}`); //http://gpt4.cocorobo.cn:8011/stream/     https://gpt4.cocorobo.cn/stream/
+      // this.source = new EventSource(
+      //   `https://claude3.cocorobo.cn/streamChat/${_uid}`
+      // );
+      this.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();
@@ -386,10 +427,10 @@ export default {
           this.chatList.find(i => i.uid == _uid).isShowSynchronization = true;
           this.chatList.find(i => i.uid == _uid).loading = false;
           this.nowChatList.push(this.chatList.find(i => i.uid == _uid));
-					let _content = this.chatList.find(i => i.uid == _uid).content;
-					if(!['智能总结','智能出题','扩展知识'].includes(_content)){
-						this.addAsk(this.chatList.find(i => i.uid == _uid).content);
-					}
+          let _content = this.chatList.find(i => i.uid == _uid).content;
+          if (!["智能总结", "智能出题", "扩展知识"].includes(_content)) {
+            this.addAsk(this.chatList.find(i => i.uid == _uid).content);
+          }
           // 这里保存对话
           this.insertChat(_uid);
           return;
@@ -417,7 +458,7 @@ export default {
     },
     //保存消息
     insertChat(_uid) {
-			if(_uid=='')return;
+      if (_uid == "") return;
       let _data = this.chatList.find(i => i.uid == _uid);
       if (!_data) return;
       let params = {
@@ -432,7 +473,7 @@ export default {
         filename: _data.filename,
         session_name: `${this.courseId}-studyStudent-md` //这是对话记录位置
       };
-			this.saveUid = ""
+      this.saveUid = "";
       this.ajax
         .post("https://gpt4.cocorobo.cn/insert_chat", params)
         .then(res => {});
@@ -532,52 +573,52 @@ export default {
           return;
         } else if (i.content == "getImage") {
           return history.push({
-            "role": "assistant",
-            "content": i.aiContent
+            role: "assistant",
+            content: i.aiContent
           });
         }
         if (i.content) {
           history.push({
-            "role": "user",
-            "content": i.content
+            role: "user",
+            content: i.content
           });
         }
         if (i.aiContent) {
           history.push({
-            "role": "assistant",
-            "content": i.aiContent
+            role: "assistant",
+            content: i.aiContent
           });
         }
       });
-      history.push({ "role": "user", "content": _text });
+      history.push({ role: "user", content: _text });
       // history.pop();
+      let params = {
+        model: "gpt-3.5-turbo",
+        temperature: 0,
+        max_tokens: 4096,
+        top_p: 1,
+        frequency_penalty: 0,
+        presence_penalty: 0,
+        messages: history,
+        uid: _uuid,
+        mind_map_question: _text
+      };
       // let params = {
-      //   model: "gpt-3.5-turbo",
-      //   temperature: 0,
-      //   max_tokens: 4096,
-      //   top_p: 1,
-      //   frequency_penalty: 0,
-      //   presence_penalty: 0,
-      //   messages: history,
+      //   message: {
+      //     anthropic_version: "bedrock-2023-05-31",
+      //     max_tokens: 4096,
+      //     temperature: 0,
+      //     top_p: 1,
+      //     messages: history
+      //   },
       //   uid: _uuid,
-      //   mind_map_question: _text
+      //   model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
       // };
-			let params = {
-        message: {
-          anthropic_version: "bedrock-2023-05-31",
-          max_tokens: 4096,
-          temperature: 0,
-          top_p: 1,
-          messages: history
-        },
-        uid: _uuid,
-        model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
-      };
       this.text = "";
 
       this.ajax
-				.post("https://claude3.cocorobo.cn/chat", params)
-        // .post("https://gpt4.cocorobo.cn/chat", params)
+        // .post("https://claude3.cocorobo.cn/chat", params)
+        .post("https://gpt4.cocorobo.cn/chat", params)
         .then(res => {
           if (res.data.FunctionResponse.result == "发送成功") {
           } else {
@@ -589,7 +630,7 @@ export default {
           console.log(e);
           this.chatLoading = false;
         });
-			this.saveUid = _uuid;
+      this.saveUid = _uuid;
       this.getAiContent(_uuid);
     },
     sendType(_text) {
@@ -632,8 +673,8 @@ Instruction: Based on the context, follow "Format example", write content.
 5. 环保教育活动:在学校中推广环保意识的活动和资源。
         `;
       } else if (_text == "智能出题") {
-				// console.log("👇")
-				// return console.log(this.navList[this.courseType].task[this.taskCount].taskName)
+        // console.log("👇")
+        // return console.log(this.navList[this.courseType].task[this.taskCount].taskName)
         _msg = `
         NOTICE
 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.
@@ -670,7 +711,7 @@ Instruction: Based on the context, follow "Format example", write content.
 
 
 					`;
-					console.log(_msg)
+        console.log(_msg);
       } else if (_text == "智能总结") {
         _msg = `
         NOTICE
@@ -760,34 +801,34 @@ Instruction: Based on the context, follow "Format example", write content.
       // history.push({ role: "user", content: _text });
       this.scrollBottom();
 
+      let params = {
+        model: "gpt-3.5-turbo",
+        temperature: 0,
+        max_tokens: 4096,
+        top_p: 1,
+        frequency_penalty: 0,
+        presence_penalty: 0,
+        messages: history,
+        uid: _uuid,
+        mind_map_question: _text
+      };
       // let params = {
-      //   model: "gpt-3.5-turbo",
-      //   temperature: 0,
-      //   max_tokens: 4096,
-      //   top_p: 1,
-      //   frequency_penalty: 0,
-      //   presence_penalty: 0,
-      //   messages: history,
+      //   message: {
+      //     anthropic_version: "bedrock-2023-05-31",
+      //     max_tokens: 4096,
+      //     temperature: 0,
+      //     top_p: 1,
+      //     messages: history
+      //   },
       //   uid: _uuid,
-      //   mind_map_question: _text
+      //   model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
       // };
-			let params = {
-        message: {
-          anthropic_version: "bedrock-2023-05-31",
-          max_tokens: 4096,
-          temperature: 0,
-          top_p: 1,
-          messages: history
-        },
-        uid: _uuid,
-        model: "Claude 3 Sonnet" // Claude 3 Sonnet或者Claude 3 Haiku
-      };
       this.text = "";
 
       this.ajax
-				.post("https://claude3.cocorobo.cn/chat", params)
+        // .post("https://claude3.cocorobo.cn/chat", params)
 
-        // .post("https://gpt4.cocorobo.cn/chat", params)
+        .post("https://gpt4.cocorobo.cn/chat", params)
         .then(res => {
           if (res.data.FunctionResponse.result == "发送成功") {
           } else {
@@ -799,12 +840,12 @@ Instruction: Based on the context, follow "Format example", write content.
           console.log(e);
           this.chatLoading = false;
         });
-				this.saveUid = _uuid;
+      this.saveUid = _uuid;
       this.getAiContent(_uuid);
       // this.send(text);
     },
-		addAsk(_text){
-			this.chatLoading = true;
+    addAsk(_text) {
+      this.chatLoading = true;
       let _uuid = uuidv4();
 
       let _msg = `NOTICE
@@ -875,7 +916,7 @@ ${_text}
 
       this.scrollBottom();
 
-			let history = [];
+      let history = [];
       // this.nowChatList.forEach(i => {
       //   if (i.content == "wanSearch") {
       //     // history.push({
@@ -890,7 +931,7 @@ ${_text}
       //     });
       //   }else if(i.content == "addAsk"){
 
-			// 	}
+      // 	}
       //   if (i.content) {
       //     history.push({
       //       role: "user",
@@ -905,7 +946,7 @@ ${_text}
       //   }
       // });
       history.push({ role: "user", content: _msg });
-			console.log(history)
+      console.log(history);
       let params = {
         model: "gpt-3.5-turbo",
         temperature: 0,
@@ -913,8 +954,8 @@ ${_text}
         top_p: 1,
         frequency_penalty: 0,
         presence_penalty: 0,
-        messages:history,
-				stream: false,
+        messages: history,
+        stream: false,
         uid: _uuid,
         mind_map_question: ""
       };
@@ -935,43 +976,70 @@ ${_text}
         .post("https://gpt4.cocorobo.cn/chat", params)
         // .post("https://claude3.cocorobo.cn/chat", params)
         .then(res => {
-					console.log(res)
-					let _data = res.data.FunctionResponse.choices[0].message.content;
-					console.log(_data);
-					this.chatList.find(i => i.uid == _uuid).aiContent =JSON.parse(_data);
-					this.chatList.find(i => i.uid == _uuid).isalltext = true;
+          console.log(res);
+          let _data = res.data.FunctionResponse.choices[0].message.content;
+          console.log(_data);
+          this.chatList.find(i => i.uid == _uuid).aiContent = JSON.parse(_data);
+          this.chatList.find(i => i.uid == _uuid).isalltext = true;
           this.chatList.find(i => i.uid == _uuid).isShowSynchronization = true;
           this.chatList.find(i => i.uid == _uuid).loading = false;
-					this.scrollBottom();
-					this.chatLoading = false;
+          this.scrollBottom();
+          this.chatLoading = false;
         })
         .catch(e => {
           this.chatLoading = false;
           console.log(e);
         });
-		},
-    getAllStudent(){
-			let params = {
-				oid:this.oid,
-				cid:this.tcid?this.tcid:this.courseDetail.juri
-			}
-			this.allStudent = []
-			this.ajax.get(this.$store.state.api+'selectWorksStudent',params).then(res=>{
-				this.allStudent = res.data[0]
-			})
-		},
+    },
+    getAllStudent() {
+      let params = {
+        oid: this.oid,
+        cid: this.tcid ? this.tcid : this.courseDetail.juri
+      };
+      this.allStudent = [];
+      this.ajax
+        .get(this.$store.state.api + "selectWorksStudent", params)
+        .then(res => {
+          this.allStudent = res.data[0];
+        });
+    },
     scrollBottom() {
       this.$nextTick(() => {
         this.$refs.chatRef.scrollTop = this.$refs.chatRef.scrollHeight;
       });
     },
-
+    download(_url) {
+        let xhr = new XMLHttpRequest();
+        xhr.open("GET", _url, true);
+        xhr.responseType = "blob";
+					xhr.onload = () => {
+          if (xhr.status === 200) {
+            let blob = xhr.response;
+            // const _blob = new Blob([blob], { type: fileType });
+            const downloadElement = document.createElement("a");
+            const url = window.URL.createObjectURL(blob);
+            downloadElement.href = url;
+            downloadElement.download = "Image.jpg";
+            downloadElement.click();
+            window.URL.revokeObjectURL(url); // 释放内存
+          }else{
+						this.$message.error("此图片不支持下载");
+					}
+        };
+				xhr.onerror = (e)=>{
+					console.log(e)
+					this.$message.error("此图片不支持下载");
+				}
+        
+        xhr.send();
+      
+    }
   },
   mounted() {
     this.getChatList().then(_ => {
       this.scrollBottom();
     });
-		this.getAllStudent();
+    this.getAllStudent();
     this.nowChatList = [];
   }
 };
@@ -1032,22 +1100,22 @@ ${_text}
   justify-content: space-between;
 }
 
-.tt_i_b_step{
-	display: flex;
-	width: 100%;
-	height: auto;
-	margin: 10px 0;
+.tt_i_b_step {
+  display: flex;
+  width: 100%;
+  height: auto;
+  margin: 10px 0;
 }
 
-.tt_i_b_step>span{
-	height: 5px;
-	background-color: #E0EAFB;
-	flex: 1;
-	border-left: 2px solid #fff;
+.tt_i_b_step > span {
+  height: 5px;
+  background-color: #e0eafb;
+  flex: 1;
+  border-left: 2px solid #fff;
 }
 
-.tt_i_b_step>span:nth-child(-1){
-	border: none;
+.tt_i_b_step > span:nth-child(-1) {
+  border: none;
 }
 
 .tt_i_box > img {
@@ -1074,7 +1142,7 @@ ${_text}
 
 .tt_i_b_box {
   width: auto;
-	min-width: 140px;
+  min-width: 140px;
   height: 60px;
   box-sizing: border-box;
   border: solid 1px #e0eafb;
@@ -1084,8 +1152,8 @@ ${_text}
 
 .tt_i_b_b_item {
   width: 70px;
-	min-width: 70px;
-	flex: 1;
+  min-width: 70px;
+  flex: 1;
   height: 100%;
   display: flex;
   flex-direction: column;
@@ -1394,13 +1462,13 @@ ${_text}
   cursor: pointer;
 }
 
-.t_b_btn>div{
-	width: 100%;
-	height:100%;
-	display:flex;
-	justify-content:center;
-	align-items:center;
-	color:#fff;
+.t_b_btn > div {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  color: #fff;
 }
 
 .t_b_btn > span {
@@ -1416,31 +1484,50 @@ th {
   padding: 10px;
 }
 
-.s_t_addAsk{
-	width:100%;
-	height:auto;
-	padding:10px 20px;
-	display:flex;
-	flex-direction:column;
-	justify-content:center;
-	align-items:center;
-	box-sizing:border-box;
+.s_t_addAsk {
+  width: 100%;
+  height: auto;
+  padding: 10px 20px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  box-sizing: border-box;
+}
+
+.s_t_addAsk > span {
+  box-sizing: border-box;
+  width: auto;
+  height: auto;
+  padding: 15px;
+  margin-bottom: 10px;
+  background-color: #f5f6f7;
+  border-radius: 10px;
+  cursor: pointer;
+  border: solid 1px #e8e9ec;
+  transition: 0.3s;
+}
+
+.s_t_addAsk > span:hover {
+  background-color: #e8e9ec;
 }
 
-.s_t_addAsk>span{
-	box-sizing:border-box;
-	width:auto;
-	height:auto;
-	padding:15px;
-	margin-bottom:10px;
-	background-color:#F5F6F7;
-	border-radius:10px;
-	cursor:pointer;
-	border:solid 1px #E8E9EC;
-	transition:.3s;
+.d_t_c_a_r_c_img:hover .download_image {
+  display: block;
 }
 
-.s_t_addAsk>span:hover{
-	background-color:#E8E9EC;
+.download_image {
+  position: absolute;
+  display: none;
+  right: 5px;
+  bottom: 5px;
+  width: 30px;
+  height: 30px;
+  cursor: pointer;
+}
+
+.download_image > img {
+  width: 100%;
+  height: 100%;
 }
 </style>

+ 100 - 0
src/components/classRoomHelper/component/timepiece.vue

@@ -0,0 +1,100 @@
+<template>
+  <div class="timepiece">
+    <div class="time" v-if="time > 0">
+      <img :src="require('../../../assets/icon/course/alarmClock.png')" />
+      <span>{{ showTime(time) }}</span>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      // time: 300,//5分钟
+      time: 0,
+      timer: null
+    };
+  },
+  watch: {
+    time(newValue) {
+      if (this.timer) {
+        if (newValue < 0) {
+          clearInterval(this.timer);
+					this.timer = null;
+          this.$alert("计时已结束", "计时器", {
+            confirmButtonText: "确定",
+            callback: action => {
+							console.log('计时结束')
+            }
+          });
+        }
+      }
+    }
+  },
+	methods: {
+		startTime(time){
+			if(this.timer)clearInterval(this.timer);
+			this.timer = null;
+			this.time = time;
+			this.timer = setInterval(()=>{
+				this.time-=1;
+			},1000)
+		}
+	},
+  computed: {
+    showTime() {
+      return duration => {
+        if (duration <= 0) return `00:00`;
+        // 更新currentTime,将秒数转换为时分秒格式
+        // let hours = Math.floor(duration / 3600);
+        let minutes = Math.floor((duration % 3600) / 60);
+        let seconds = Math.floor(duration % 60);
+        // this.recordedForm.time = `${hours.toString().padStart(2, "0")}:${minutes
+        // 	.toString()
+        // 	.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
+        return `${minutes
+          .toString()
+          .padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
+      };
+    }
+  }
+};
+</script>
+
+<style scoped>
+.timepiece {
+  position: fixed;
+  width: auto;
+  height: auto;
+  top: 140px;
+  right: 10px;
+  z-index: 9999;
+  display: flex;
+}
+
+.time {
+  width: auto;
+  height: 80px;
+  display: flex;
+  align-items: center;
+}
+
+.time > img {
+  width: 55px;
+  height: 55px;
+  z-index: 9;
+}
+.time > span {
+  background-color: #38d0b1;
+  border: solid 1px #1a9200;
+  border-radius: 8px;
+  position: relative;
+  left: -20px;
+  top: 3px;
+  padding: 5px 10px 5px 25px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+</style>

+ 21 - 6
src/components/classRoomHelper/index.vue

@@ -150,7 +150,8 @@
         </div>
       </div>
     </div>
-		<levitatedSphere/>
+		<levitatedSphere @startTime="startTime"/>
+		<timepiece ref="timepieceRef"/>
   </div>
 </template>
 
@@ -159,13 +160,15 @@ import searchArea from "./component/searchArea.vue";
 import taskArea from "./component/taskArea.vue";
 import dialogArea from "./component/dialogArea.vue";
 import levitatedSphere from './component/levitatedSphere.vue'
+import timepiece from "./component/timepiece.vue";
 export default {
   emits: ["refresh", "goStep", "backPage", "authority", "review"],
   components: {
     searchArea,
     taskArea,
     dialogArea,
-		levitatedSphere
+		levitatedSphere,
+		timepiece
   },
   props: {
     courseDetail: {
@@ -232,8 +235,17 @@ export default {
         // }
         this.itemType = type;
       });
-    }
-  }
+    },
+		//计时
+		startTime(time){
+			this.$refs.timepieceRef.startTime(time);
+		}
+  },
+	mounted(){
+		// setTimeout(()=>{
+		// 	this.startTime(10)
+		// },2000)
+	}
 };
 </script>
 
@@ -257,8 +269,10 @@ export default {
   width: 65px;
   display: flex;
   flex-direction: column;
-  justify-content: flex-end;
+  /* justify-content: flex-end; */
+	margin-top: auto;
   align-items: center;
+	overflow: auto;
 }
 
 .ch_content_box {
@@ -274,7 +288,7 @@ export default {
   border-top: solid 1px #eaeaea;
   display: flex;
   flex-direction: column;
-  justify-content: flex-end;
+  /* justify-content: flex-end; */
 }
 
 .ch_nav_box_middle {
@@ -339,6 +353,7 @@ export default {
 .ch_nav_box_top {
   width: 100%;
   height: auto;
+	margin-top: auto;
 }
 .ch_nav_box_top > div {
   width: 100%;