SanHQin 2 달 전
부모
커밋
84bf8046a6

+ 1 - 1
dist/index.html

@@ -32,7 +32,7 @@
       width: 100%;
       background: #e6eaf0;
       font-family: '黑体';
-    }</style><link href=./static/css/app.8f19f756a441c1214dc059ca3531cf96.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.9811ebe9d5c4458a1b2a.js></script><script type=text/javascript src=./static/js/vendor.bb486323f0fa002ba2e7.js></script><script type=text/javascript src=./static/js/app.8210568261f9561a212b.js></script></body></html><script>function stopSafari() {
+    }</style><link href=./static/css/app.b3b54293cb15652faa8e512ce218aac4.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.161e82026ac2ae03ab6f.js></script><script type=text/javascript src=./static/js/vendor.bb486323f0fa002ba2e7.js></script><script type=text/javascript src=./static/js/app.ce06d3c7064b7d5f54f2.js></script></body></html><script>function stopSafari() {
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
dist/static/css/app.b3b54293cb15652faa8e512ce218aac4.css


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
dist/static/css/app.b3b54293cb15652faa8e512ce218aac4.css.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
dist/static/js/0.4f3b05586c3acc102a54.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
dist/static/js/0.4f3b05586c3acc102a54.js.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
dist/static/js/0.df8814bab917ab2583e0.js.map


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
dist/static/js/app.ce06d3c7064b7d5f54f2.js


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
dist/static/js/app.ce06d3c7064b7d5f54f2.js.map


+ 2 - 2
dist/static/js/manifest.9811ebe9d5c4458a1b2a.js → dist/static/js/manifest.161e82026ac2ae03ab6f.js

@@ -1,2 +1,2 @@
-!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,a,c){for(var i,u,f,s=0,l=[];s<r.length;s++)u=r[s],t[u]&&l.push(t[u][0]),t[u]=0;for(i in a)Object.prototype.hasOwnProperty.call(a,i)&&(e[i]=a[i]);for(n&&n(r,a,c);l.length;)l.shift()();if(c)for(s=0;s<c.length;s++)f=o(o.s=c[s]);return f};var r={},t={6:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var a=document.getElementsByTagName("head")[0],c=document.createElement("script");c.type="text/javascript",c.charset="utf-8",c.async=!0,c.timeout=12e4,o.nc&&c.setAttribute("nonce",o.nc),c.src=o.p+"static/js/"+e+"."+{0:"df8814bab917ab2583e0",1:"14e8e8c7e44fc858e4a6",2:"94e1427bfc7ef0b4c685",3:"3a9f53a78da16650e6b8"}[e]+".js";var i=setTimeout(u,12e4);function u(){c.onerror=c.onload=null,clearTimeout(i);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return c.onerror=c.onload=u,a.appendChild(c),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="./",o.oe=function(e){throw console.error(e),e}}([]);
-//# sourceMappingURL=manifest.9811ebe9d5c4458a1b2a.js.map
+!function(e){var n=window.webpackJsonp;window.webpackJsonp=function(r,c,a){for(var i,u,f,s=0,l=[];s<r.length;s++)u=r[s],t[u]&&l.push(t[u][0]),t[u]=0;for(i in c)Object.prototype.hasOwnProperty.call(c,i)&&(e[i]=c[i]);for(n&&n(r,c,a);l.length;)l.shift()();if(a)for(s=0;s<a.length;s++)f=o(o.s=a[s]);return f};var r={},t={6:0};function o(n){if(r[n])return r[n].exports;var t=r[n]={i:n,l:!1,exports:{}};return e[n].call(t.exports,t,t.exports,o),t.l=!0,t.exports}o.e=function(e){var n=t[e];if(0===n)return new Promise(function(e){e()});if(n)return n[2];var r=new Promise(function(r,o){n=t[e]=[r,o]});n[2]=r;var c=document.getElementsByTagName("head")[0],a=document.createElement("script");a.type="text/javascript",a.charset="utf-8",a.async=!0,a.timeout=12e4,o.nc&&a.setAttribute("nonce",o.nc),a.src=o.p+"static/js/"+e+"."+{0:"4f3b05586c3acc102a54",1:"14e8e8c7e44fc858e4a6",2:"94e1427bfc7ef0b4c685",3:"3a9f53a78da16650e6b8"}[e]+".js";var i=setTimeout(u,12e4);function u(){a.onerror=a.onload=null,clearTimeout(i);var n=t[e];0!==n&&(n&&n[1](new Error("Loading chunk "+e+" failed.")),t[e]=void 0)}return a.onerror=a.onload=u,c.appendChild(a),r},o.m=e,o.c=r,o.d=function(e,n,r){o.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},o.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return o.d(n,"a",n),n},o.o=function(e,n){return Object.prototype.hasOwnProperty.call(e,n)},o.p="./",o.oe=function(e){throw console.error(e),e}}([]);
+//# sourceMappingURL=manifest.161e82026ac2ae03ab6f.js.map

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
dist/static/js/manifest.161e82026ac2ae03ab6f.js.map


+ 4 - 0
src/assets/icon/classroomObservation/isDoStatus_icon.svg

@@ -0,0 +1,4 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path opacity="0.5" d="M7.00008 1.16663C5.84636 1.16663 4.71854 1.50875 3.75926 2.14972C2.79997 2.79069 2.0523 3.70174 1.61079 4.76764C1.16928 5.83354 1.05376 7.00643 1.27884 8.13799C1.50392 9.26954 2.05949 10.3089 2.87529 11.1247C3.6911 11.9406 4.7305 12.4961 5.86206 12.7212C6.99361 12.9463 8.1665 12.8308 9.2324 12.3893C10.2983 11.9477 11.2093 11.2001 11.8503 10.2408C12.4913 9.2815 12.8334 8.15368 12.8334 6.99996C12.8334 6.23391 12.6825 5.47537 12.3894 4.76764C12.0962 4.05991 11.6665 3.41684 11.1249 2.87517C10.5832 2.33349 9.94014 1.90381 9.2324 1.61066C8.52467 1.31751 7.76613 1.16663 7.00008 1.16663ZM7.00008 11.6666C6.0771 11.6666 5.17485 11.3929 4.40742 10.8801C3.63999 10.3674 3.04186 9.63854 2.68865 8.78581C2.33544 7.93309 2.24302 6.99478 2.42309 6.08954C2.60315 5.18429 3.04761 4.35277 3.70025 3.70013C4.3529 3.04748 5.18442 2.60303 6.08966 2.42296C6.99491 2.2429 7.93322 2.33531 8.78594 2.68852C9.63866 3.04173 10.3675 3.63987 10.8803 4.4073C11.3931 5.17473 11.6667 6.07698 11.6667 6.99996C11.6667 8.23764 11.1751 9.42462 10.2999 10.2998C9.42475 11.175 8.23776 11.6666 7.00008 11.6666Z" fill="#3681FC"/>
+<path d="M11.6667 6.99996L12.8333 6.99996C12.8333 6.23392 12.6825 5.47537 12.3893 4.76764C12.0961 4.05991 11.6665 3.41685 11.1248 2.87517C10.5831 2.33349 9.94005 1.90381 9.23232 1.61066C8.52459 1.31751 7.76604 1.16663 7 1.16663V2.33329C8.23768 2.33329 9.42466 2.82496 10.2998 3.70013C11.175 4.5753 11.6667 5.76228 11.6667 6.99996Z" fill="#3681FC"/>
+</svg>

+ 10 - 0
src/assets/icon/classroomObservation/successStatus_icon.svg

@@ -0,0 +1,10 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2421_25813)">
+<path d="M7 0.875C5.78859 0.875 4.60439 1.23423 3.59713 1.90725C2.58988 2.58027 1.80483 3.53687 1.34124 4.65606C0.877654 5.77526 0.756358 7.00679 0.992693 8.19493C1.22903 9.38306 1.81238 10.4744 2.66897 11.331C3.52557 12.1876 4.61694 12.771 5.80507 13.0073C6.99321 13.2436 8.22474 13.1223 9.34394 12.6588C10.4631 12.1952 11.4197 11.4101 12.0928 10.4029C12.7658 9.39562 13.125 8.21141 13.125 7C13.125 5.37555 12.4797 3.81763 11.331 2.66897C10.1824 1.52031 8.62445 0.875 7 0.875ZM6.125 9.44563L3.9375 7.25813L4.63313 6.5625L6.125 8.05437L9.36688 4.8125L10.0651 5.50638L6.125 9.44563Z" fill="#BCE685"/>
+</g>
+<defs>
+<clipPath id="clip0_2421_25813">
+<rect width="14" height="14" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 4 - 0
src/assets/icon/classroomObservation/waitStatus_icon.svg

@@ -0,0 +1,4 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path opacity="0.5" d="M7.00008 1.16663C5.84636 1.16663 4.71854 1.50875 3.75926 2.14972C2.79997 2.79069 2.0523 3.70174 1.61079 4.76764C1.16928 5.83354 1.05376 7.00643 1.27884 8.13799C1.50392 9.26954 2.05949 10.3089 2.87529 11.1247C3.6911 11.9406 4.7305 12.4961 5.86206 12.7212C6.99361 12.9463 8.1665 12.8308 9.2324 12.3893C10.2983 11.9477 11.2093 11.2001 11.8503 10.2408C12.4913 9.2815 12.8334 8.15368 12.8334 6.99996C12.8334 6.23391 12.6825 5.47537 12.3894 4.76764C12.0962 4.05991 11.6665 3.41684 11.1249 2.87517C10.5832 2.33349 9.94014 1.90381 9.2324 1.61066C8.52467 1.31751 7.76613 1.16663 7.00008 1.16663ZM7.00008 11.6666C6.0771 11.6666 5.17485 11.3929 4.40742 10.8801C3.63999 10.3674 3.04186 9.63854 2.68865 8.78581C2.33544 7.93309 2.24302 6.99478 2.42309 6.08954C2.60315 5.18429 3.04761 4.35277 3.70025 3.70013C4.3529 3.04748 5.18442 2.60303 6.08966 2.42296C6.99491 2.2429 7.93322 2.33531 8.78594 2.68852C9.63866 3.04173 10.3675 3.63987 10.8803 4.4073C11.3931 5.17473 11.6667 6.07698 11.6667 6.99996C11.6667 8.23764 11.1751 9.42462 10.2999 10.2998C9.42475 11.175 8.23776 11.6666 7.00008 11.6666Z" fill="#969BA3"/>
+<path d="M11.6667 6.99996L12.8333 6.99996C12.8333 6.23392 12.6825 5.47537 12.3893 4.76764C12.0961 4.05991 11.6665 3.41685 11.1248 2.87517C10.5831 2.33349 9.94005 1.90381 9.23232 1.61066C8.52459 1.31751 7.76604 1.16663 7 1.16663V2.33329C8.23768 2.33329 9.42466 2.82496 10.2998 3.70013C11.175 4.5753 11.6667 5.76228 11.6667 6.99996Z" fill="#969BA3"/>
+</svg>

+ 4 - 3
src/components/pages/classroomObservation/components/analysisItem.vue

@@ -500,7 +500,7 @@ export default {
                 _copyData.jsonData
               );
             }
-            this.changeShowIndex(1);
+            this.changeShowIndex(1,false);
             this.loading = false;
             this.$nextTick(() => {
               if (
@@ -586,7 +586,7 @@ export default {
           console.log("取消");
         });
     },
-    changeShowIndex(value) {
+    changeShowIndex(value,save=true) {
       if (this.historyResult.length == 0) return;
       if (value == -1) {
         if (this.showIndex > 0) this.showIndex--;
@@ -602,7 +602,8 @@ export default {
       if (JSON.stringify(_copyData) == JSON.stringify(_oldCopyDate)) return;
       // this.data = _copyData;
       this.$emit("editItem", this.data.id, _copyData);
-      this.$emit("saveItem", this.data.id, _copyData);
+      console.log("save👉",save)
+      if(save)this.$emit("saveItem", this.data.id, _copyData);
     },
     // editTitle(){
     // 	this.$refs.editNameDialogRef.open(this.data.jsonData.anotherName?this.data.jsonData.anotherName:this.data.jsonData.name)

+ 5 - 5
src/components/pages/classroomObservation/components/analysisSpecialItem.vue

@@ -832,7 +832,7 @@ CH:${_CH}
               _copyData.jsonData
             );
           }
-          this.changeShowIndex(1);
+          this.changeShowIndex(1,false);
           this.loading = false;
 					this.$nextTick(()=>{
 
@@ -858,7 +858,7 @@ CH:${_CH}
           console.log("取消");
         });
     },
-    changeShowIndex(value) {
+    changeShowIndex(value,save=true) {
       if (this.historyResult.length == 0) return;
       if (value == -1) {
         if (this.showIndex > 0) this.showIndex--;
@@ -874,7 +874,8 @@ CH:${_CH}
       if (JSON.stringify(_copyData) == JSON.stringify(_oldCopyDate)) return;
       // this.data = _copyData;
       this.$emit("editItem", this.data.id, _copyData);
-      this.$emit("saveItem", this.data.id, _copyData);
+      console.log("save👉",save)
+      if(save)this.$emit("saveItem", this.data.id, _copyData);
     },
     convertToSeconds(time) {
       let parts = time.split(":");
@@ -1128,10 +1129,9 @@ CH:${_CH}
                 _copyData.jsonData
               );
             }
-            this.changeShowIndex(1);
+            this.changeShowIndex(1,false);
             this.loading = false;
 						this.$nextTick(()=>{
-
 							this.editEcharts(true);
 						})
           })

+ 561 - 39
src/components/pages/classroomObservation/dialog/batchCreationClassDialog.vue

@@ -47,7 +47,7 @@
                     i =>
                       selectStatus == "99" ||
                       statusList
-                        .find(i => i.value == selectStatus)
+                        .find(i => i.value === selectStatus)
                         .allow.includes(i.state)
                   ).length
                 }}</span
@@ -82,6 +82,7 @@
               @changeChecked="changeCardChecked"
               @editBaseMessage="editBaseMessage"
               @changeData="changeData"
+              @taskBtn="taskBtn"
             />
           </div>
         </div>
@@ -103,7 +104,9 @@ import batchClassCard from "../newComponents/batchClassCard.vue";
 import uploadFileToCreateClassDialog from "./uploadFileToCreateClassDialog.vue";
 import editBaseMessageDialog from "./editBaseMessageDialog.vue";
 import { v4 as uuidv4 } from "uuid";
+import { toolMixin } from "../tools/mixin";
 export default {
+  mixins: [toolMixin],
   components: {
     batchClassCard,
     uploadFileToCreateClassDialog,
@@ -116,13 +119,15 @@ export default {
         { label: "已完成", value: "2", allow: ["2"] },
         { label: "处理中", value: "1", allow: ["1"] },
         { label: "等待处理", value: "0", allow: ["0"] },
-        { label: "停止中",value:"3",allow:['3']}
+        { label: "待开始", value: "3", allow: ["3"] }
       ],
       userId: this.$route.query["userid"],
       selectStatus: "99",
       show: false,
       selectList: [],
-      dataList: []
+      dataList: [],
+      nowDoTaskId: [],
+      dialogTagDataList: []
     };
   },
   computed: {
@@ -227,6 +232,8 @@ export default {
           { value: 1, name: "学科课堂分析", loading: false },
           { value: 2, name: "扩展分析", loading: false }
         ];
+      }else{
+        tagList = tagList.dialogTagList;
       }
 
       let batch = uuidv4();
@@ -260,6 +267,7 @@ export default {
                 fileList1: [],
                 fileList2: [],
                 fileList3: [],
+                name:"课堂记录",
                 NephogramList: [],
                 videoList: []
               }
@@ -295,6 +303,12 @@ export default {
               text: "生成报告",
               status: "0",
               progress: "0"
+            },
+            {
+              type: "createClass",
+              text: "创建课堂",
+              status: "0",
+              progress: "0"
             }
           ];
         } else if (i.type == "audio/wav") {
@@ -317,25 +331,39 @@ export default {
               text: "文本转录",
               status: "0",
               progress: "0"
-            },
-            {
+            }
+          ];
+
+          if (data.json.automaticCoding) {
+            data.jsonData.steps.push({
               type: "automaticCoding",
               text: "自动编码",
               status: "0",
               progress: "0"
-            },
-            {
-              type: "getFileIds",
-              text: "获取文件fileid",
-              status: "0"
-            },
-            {
-              type: "generateReport",
-              text: "生成报告",
-              status: "0",
-              progress: "0"
-            }
-          ];
+            });
+          }
+
+          data.jsonData.steps.push(
+            ...[
+              {
+                type: "getFileIds",
+                text: "获取文件fileid",
+                status: "0"
+              },
+              {
+                type: "generateReport",
+                text: "生成报告",
+                status: "0",
+                progress: "0"
+              },
+              {
+                type: "createClass",
+                text: "创建课堂",
+                status: "0",
+                progress: "0"
+              }
+            ]
+          );
         } else if (i.type == "video/mp4") {
           data.jsonData.baseMessage.imageList.videoList = [
             {
@@ -362,29 +390,44 @@ export default {
               text: "文本转录",
               status: "0",
               progress: "0"
-            },
-            {
+            }
+          ];
+          if (data.jsonData.automaticCoding) {
+            data.jsonData.steps.push({
               type: "automaticCoding",
               text: "自动编码",
               status: "0",
               progress: "0"
-            },
-            {
-              type: "getFileIds",
-              text: "获取文件fileid",
-              status: "0"
-            },
-            {
-              type: "generateReport",
-              text: "生成报告",
-              status: "0",
-              progress: "0"
-            }
-          ];
+            });
+          }
+          data.jsonData.steps.push(
+            ...[
+              {
+                type: "getFileIds",
+                text: "获取文件fileid",
+                status: "0"
+              },
+              {
+                type: "generateReport",
+                text: "生成报告",
+                status: "0",
+                progress: "0"
+              },
+              {
+                type: "createClass",
+                text: "创建课堂",
+                status: "0",
+                progress: "0"
+              }
+            ]
+          );
         }
 
         promiseList.push(
           new Promise((resolve, reject) => {
+            // 本地保存fileObj
+            const fileObj = data.jsonData.fileData.fileObj;
+            delete data.jsonData.fileData.fileObj;
             const params = [
               {
                 remarks: data.remarks,
@@ -401,6 +444,7 @@ export default {
               .then(res => {
                 const _data = res.data[0][0];
                 if (_data.id) {
+                  data.jsonData.fileData.fileObj = fileObj;
                   data.id = _data.id;
                   this.dataList.push(data);
                 }
@@ -527,19 +571,46 @@ export default {
           }
         });
     },
+    //获取所有的模块分析
+    getDialogTagDataList() {
+      let params = {
+        uid: this.userId
+      };
+      this.ajax
+        .get(this.$store.state.api + "select_smodel", params)
+        .then(res => {
+          let _result = res.data[0];
+          this.dialogTagDataList = _result;
+        })
+        .catch(e => {
+          console.log(e);
+          this.$message.error("获取模块分析列表失败");
+        });
+    },
     updateTask(id) {
       return new Promise((resolve, reject) => {
         let _data = this.dataList.find(i => i.id === id);
         if (_data) {
+          const dataCopy = JSON.parse(JSON.stringify(_data));
+          if (dataCopy.jsonData.fileData.fileObj) {
+            delete _data.jsonData.fileData.fileObj;
+          }
+          if (dataCopy.jsonData.baseMessage.editorBarData && dataCopy.jsonData.baseMessage.editorBarData.content) {
+            delete dataCopy.jsonData.baseMessage.editorBarData.content;
+          }
+
+          console.log("dataCopy", dataCopy);
           let params = [
             {
-              id: _data.id,
+              id: dataCopy.id,
               userId: this.userId,
-              remarks: _data.remarks,
-              status: _data.status,
-              json: JSON.stringify(_data.jsonData),
-              createId: _data.createId,
-              batch: _data.batch
+              remarks: dataCopy.remarks,
+              status: ["0", "1"].includes(dataCopy.status)
+                ? "3"
+                : dataCopy.status,
+              json: JSON.stringify(dataCopy.jsonData),
+              createId: dataCopy.createId,
+              batch: dataCopy.batch
             }
           ];
 
@@ -570,10 +641,461 @@ export default {
           this.updateTask(this.dataList[_index].id);
         }
       }
+    },
+    taskBtn(obj) {
+      let { type, id } = obj;
+      if (type === "startTask" && id) {
+        // this.startTask(id);
+        if (
+          this.dataList.some(i => i.status === "1") &&
+          this.dataList.find(i => i.id === id).status === "0"
+        ) {
+          this.$confirm(
+            "您确定要开始该任务嘛?其他已开始的任务会暂停。",
+            "提示",
+            {
+              confirmButtonText: "确定",
+              cancelButtonText: "取消",
+              type: "warning"
+            }
+          )
+            .then(() => {
+              this.startTask(id);
+            })
+            .catch(() => {
+              console.log("不暂停");
+            });
+        } else {
+          this.startTask(id);
+        }
+      } else if (type === "stopTask" && id) {
+        this.$confirm("您确定要停止该任务吗?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        })
+          .then(() => {
+            this.stopTask(id);
+          })
+          .catch(() => {
+            console.log("不暂停");
+          });
+      } else if (type === "pauseTask" && id) {
+        this.$confirm("您确定要暂停该任务吗?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        })
+          .then(() => {
+            this.pauseTask(id);
+          })
+          .catch(() => {
+            console.log("不暂停");
+          });
+      }
+    },
+    async startTask(id) {
+      let _isStartTaskIndex = this.dataList.findIndex(i => i.status === "1");
+      let _needStartTaskIndex = this.dataList.findIndex(i => i.id === id);
+      if (_isStartTaskIndex === -1) {
+        //没有任务在进行,直接开始处理
+        this.dataList[_needStartTaskIndex].status = "1"; //处理中
+      } else if (
+        this.dataList[_needStartTaskIndex].status === "3" &&
+        _isStartTaskIndex !== -1
+      ) {
+        //有任务在进行,且这个任务是从待开始状态到开始状态,等待处理
+        this.dataList[_needStartTaskIndex].status = "0";
+      } else if (
+        _isStartTaskIndex !== -1 &&
+        this.dataList[_needStartTaskIndex].status === "0"
+      ) {
+        //有任务在进行,且这个任务是从等待状态到开始状态,停止现任务,开始新任务
+        this.nowDoTaskId = this.nowDoTaskId.filter(
+          i => i !== this.dataList[_isStartTaskIndex].id
+        );
+        this.dataList[_isStartTaskIndex].status = "0"; //
+        this.dataList[_needStartTaskIndex].status = "1"; //处理中
+      }
+
+      if (
+        !this.nowDoTaskId.includes(this.dataList.find(i => i.status === "1").id)
+      ) {
+        let _startData = JSON.parse(
+          JSON.stringify(this.dataList.find(i => i.status === "1"))
+        );
+
+        if (
+          this.dataList.find(i => i.id === _startData.id).jsonData.fileData
+            .fileObj
+        ) {
+          _startData.jsonData.fileData.fileObj = this.dataList.find(
+            i => i.id === _startData.id
+          ).jsonData.fileData.fileObj;
+        }
+        this.nowDoTaskId.push(_startData.id);
+        let _stepList = _startData.jsonData.steps;
+        for (let i = 0; i < _stepList.length; i++) {
+          //按顺序处理任务
+          let _step = _stepList[i];
+          console.log(_step);
+          if (_step.status === "1") continue;
+          if (_step.type === "uploadFile") continue;
+          if (_step.type === "getVideoVoice") {
+            //视频提取音频
+            _stepList[i].status = "2";
+            this.dataList.find(
+              i => i.id === _startData.id
+            ).jsonData.steps = _stepList;
+            let _fileData = _startData.jsonData.fileData;
+            console.log("👉", _fileData);
+            let audioFile = await this.getVideoToVoiceAndUploadMixin(_fileData);
+            console.log("👉", audioFile);
+            _startData.jsonData.fileData.videoUrl = _fileData.url;
+            _startData.jsonData.fileData.url = audioFile.Location;
+            _startData.jsonData.baseMessage.imageList.fileList = [
+              {
+                name: _fileData.name,
+                status: "success",
+                url: audioFile.Location,
+                uid: "1"
+              }
+            ];
+            _stepList[i].status = "1";
+            this.dataList.find(i => i.id === _startData.id).jsonData =
+              _startData.jsonData;
+            this.updateTask(_startData.id);
+            _startData.jsonData = JSON.parse(
+              JSON.stringify(
+                this.dataList.find(i => i.id === _startData.id).jsonData
+              )
+            );
+            // console.log(`${_startData.id}:已完成视频转音频`);
+          } else if (_step.type === "transcription") {
+            //文本转录
+            const fileData = _startData.jsonData.fileData;
+            if (fileData.type === "text/plain") {
+              //txt文件
+              _stepList[i].status = "2";
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.steps = _stepList;
+              let { editorBarData } = await this.getTextContentMixin(fileData);
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.baseMessage.editorBarData = editorBarData;
+              _stepList[i].status = "1";
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.steps = _stepList;
+              this.updateTask(_startData.id);
+              _startData.jsonData = JSON.parse(
+                JSON.stringify(
+                  this.dataList.find(i => i.id === _startData.id).jsonData
+                )
+              );
+              console.log(`${_startData.id}:已获取转录文稿(txt)`);
+            } else if (
+              fileData.type === "video/mp4" ||
+              fileData.type === "audio/wav"
+            ) {
+              //mp4与wav文件
+              _stepList[i].status = "2";
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.steps = _stepList;
+              // let { editorBarData } = await this.getTextContentMixin(fileData);
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.baseMessage.editorBarData = {
+                content: "xxxxxx",
+                url: "xxxxxxx"
+              };
+              _stepList[i].status = "1";
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.steps = _stepList;
+              this.updateTask(_startData.id);
+              _startData.jsonData = JSON.parse(
+                JSON.stringify(
+                  this.dataList.find(i => i.id === _startData.id).jsonData
+                )
+              );
+              console.log(`${_startData.id}:已获取转录文稿(mp4/wav)`);
+            }
+          } else if (_step.type === "automaticCoding") {
+            if (_startData.jsonData.automaticCoding) {
+              console.log("开始自动编码");
+            } else {
+              _stepList[i].status = "1";
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.steps = _stepList;
+              this.updateTask(_startData.id);
+              _startData.jsonData = JSON.parse(
+                JSON.stringify(
+                  this.dataList.find(i => i.id === _startData.id).jsonData
+                )
+              );
+            }
+          } else if (_step.type === "getFileIds") {
+            //文件获取fileId
+            _stepList[i].status = "2";
+            this.dataList.find(
+              i => i.id === _startData.id
+            ).jsonData.steps = _stepList;
+            let _fileData = _startData.jsonData.fileData;
+            let { fileId } = await this.getFileIdMixin(_fileData.url);
+            this.dataList.find(
+              i => i.id === _startData.id
+            ).jsonData.file_ids = fileId;
+            _stepList[i].status = "1";
+            this.dataList.find(
+              i => i.id === _startData.id
+            ).jsonData.steps = _stepList;
+            this.updateTask(_startData.id);
+            _startData.jsonData = JSON.parse(
+              JSON.stringify(
+                this.dataList.find(i => i.id === _startData.id).jsonData
+              )
+            );
+            console.log(`${_startData.id}:已获取fileId`);
+          } else if (_step.type === "generateReport") {
+            //生成报告
+            _stepList[i].status = "2";
+            this.dataList.find(
+              i => i.id === _startData.id
+            ).jsonData.steps = _stepList;
+            let _analysisList = _startData.jsonData.analysisList;
+
+            let promises = [];
+            let _content =
+              _startData.jsonData.baseMessage.editorBarData.content;
+            if (!_content) {
+              _content = await this.getFile(
+                _startData.jsonData.baseMessage.editorBarData.url
+              );
+              _content = _content.data;
+              _startData.jsonData.baseMessage.editorBarData.content = _content;
+            }
+            promises = _analysisList.map((i, index) => {
+              return new Promise(async resolve => {
+                _startData.jsonData.analysisList[index].status = "default"; //默认状态
+                let _num = 0;
+                let _maxNum = 5;
+                const getAnalysis = async () => {
+                  if (
+                    ["tryAgainMax", "noAgentData"].includes(
+                      _startData.jsonData.analysisList[index].status
+                    )
+                  ) {
+                    return resolve(_startData.jsonData.analysisList[index]);
+                  }
+                  if (_num >= _maxNum) {
+                    _startData.jsonData.analysisList[index].status =
+                      "tryAgainMax"; //超过尝试次数
+                    return resolve();
+                  }
+                  _num += 1;
+                  let _assistantData =
+                    this.dialogTagDataList.find(
+                      i2 => i2.id == i.jsonData.mId
+                    ) ||
+                    this.dialogTagDataList.find(
+                      i2 => i2.name == i.jsonData.name
+                    );
+
+                  if (!_assistantData) {
+                    _startData.jsonData.analysisList[index].status =
+                      "noAgentData"; //无对应的智能体
+                    return resolve();
+                  }
+                  let data = await this.getAnalysisMixin({
+                    fileId: _startData.jsonData.file_ids,
+                    content: _content,
+                    analysisData: i.jsonData,
+                    assistantData: _assistantData,
+                    baseMessage: _startData.jsonData.baseMessage
+                  });
+                  if (data.data == 1) {
+                    if (_num < _maxNum) {
+                      //重试
+                      getAnalysis();
+                    }
+                  } else {
+                    _startData.jsonData.analysisList[index].status = "success";
+                    _startData.jsonData.analysisList[index].jsonData =
+                      data.data;
+                    resolve();
+                  }
+                };
+                getAnalysis();
+              });
+            });
+
+            await Promise.all(promises).then(() => {
+              console.log(`${_startData.id}:已完成分析获取`);
+              console.log(_startData.jsonData.analysisList)
+                _stepList[i].status = "1";
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.steps = _stepList;
+
+              this.updateTask(_startData.id);
+              _startData.jsonData = JSON.parse(
+                JSON.stringify(
+                  this.dataList.find(i => i.id === _startData.id).jsonData
+                )
+              );
+            });
+          } else if (_step.type === "createClass") {
+            //创建课堂
+            _stepList[i].status = "2";
+            this.dataList.find(
+              i => i.id === _startData.id
+            ).jsonData.steps = _stepList;
+
+            //创建课堂   赋值好fileid   替换创建后课堂的基础数据  继续下一个任务
+
+
+            let newOption = await this.createClassMixin(_startData.jsonData);
+
+            this.$emit("addNewCourseOption",newOption.data);
+            this.dataList.find(
+              i => i.id === _startData.id
+            ).jsonData.createId = newOption.tid;
+            _stepList[i].status = "1";
+            this.dataList.find(
+              i => i.id === _startData.id
+            ).jsonData.steps = _stepList;
+            this.dataList.find(
+              i => i.id === _startData.id
+            ).status = "2";
+            this.updateTask(_startData.id);
+            _startData.jsonData = JSON.parse(
+              JSON.stringify(
+                this.dataList.find(i => i.id === _startData.id).jsonData
+              )
+            );
+            console.log(`${_startData.id}:已创建课堂`);
+            // 查看是否有需要开启的任务
+            if(this.dataList.find(i=>i.status=='0')){
+              let id = this.dataList.find(i=>i.status=='0').id;
+              this.startTask(id);
+            }
+
+          //   let _analysisList = _startData.jsonData.analysisList;
+          //   let createJson = _analysisList.map(i=>{return {
+          //     jsonData: i.jsonData,
+          //     type: i.Type,
+          //     index: i.tIndex
+          //   }})
+          //   var OpenCC = require("opencc-js");
+          //   let converter = OpenCC.Converter({
+          //     from: "hk",
+          //     to: "cn"
+          //   });
+          //   createJson = createJson.filter(
+          //     i =>
+          //       !i.isOtherData &&
+          //       converter(i.jsonData.name) != converter("词频词汇分析")
+          //   );
+          //   let params = {
+          //     tid: uuidv4(),
+          //     userid: this.userId,
+          //     template: createJson
+          //   }
+          //   this.ajax
+          // .post(
+          //   "https://gpt4.cocorobo.cn/insert_classroom_observation_template",
+          //   params
+          // )
+          // .then(res => {
+          //   let _data = res.data.FunctionResponse;
+          //   if (converter(_data.message) == converter("创建成功")) {
+          //     this.ajax
+          //       .post("https://gpt4.cocorobo.cn/insert_classroom_observation", {
+          //         tid: params.tid,
+          //         type: 10,
+          //         index: 0,
+          //         json_data: JSON.stringify({ file_ids: _startData.jsonData.file_ids }),
+          //         userid: this.userId
+          //       })
+          //       .then(res2 => {
+          //         let _data2 = res2.data.FunctionResponse;
+          //         if (converter(_data2.message) == converter("创建成功")) {
+          //           let newOption = {id:uuidv4(),label:_startData.jsonData.baseMessage.name,value:params.tid}
+          //           console.log(newOption);
+
+          //           this.$emit("addNewCourseOption",newOption);
+          //           _stepList[i].status = "1";
+          //           this.dataList.find(
+          //             i => i.id === _startData.id
+          //           ).jsonData.steps = _stepList;
+          //           this.dataList.find(
+          //             i => i.id === _startData.id
+          //           ).status = "2";
+          //           this.updateTask(_startData.id);
+          //           _startData.jsonData = JSON.parse(
+          //             JSON.stringify(
+          //               this.dataList.find(i => i.id === _startData.id).jsonData
+          //             )
+          //           );
+          //           console.log(`${_startData.id}:已创建课堂`);
+
+          //           // 查看是否有需要开启的任务
+          //           if(this.dataList.find(i=>i.status=='0')){
+          //             let id = this.dataList.find(i=>i.status=='0').id;
+          //             this.startTask(id);
+          //           }
+          //         }
+          //       }).catch(err=>{
+          //         console.log("存储fileId失败")
+          //         console.log(err)
+          //       })
+          //   }
+          // }).catch(err=>{
+          //   console.log("创建课堂失败")
+          //   console.log(err)
+          // })
+
+
+
+
+          }
+        }
+      }
+    },
+    // 暂停任务
+    pauseTask(id) {
+      let _waitTask = this.dataList.find(i => i.status === "0");
+      if (_waitTask) {
+        //暂停这个任务,
+        this.dataList.find(i => i.id === id).status = "0";
+        // 开始另一个任务
+        this.startTask(_waitTask.id);
+      } else {
+        //没有任务了,所以改为待开始(停止)
+        this.dataList.find(i => i.id === id).status = "3";
+      }
+    },
+    //停止任务
+    stopTask(id) {
+      let _waitTask = this.dataList.find(i => i.status === "0");
+      if (_waitTask) {
+        //停止这个任务,
+        this.dataList.find(i => i.id === id).status = "3";
+        // 开始另一个任务
+        this.startTask(_waitTask.id);
+      } else {
+        //只停止这个任务
+        this.dataList.find(i => i.id === id).status = "3";
+      }
     }
   },
   mounted() {
     this.getTaskList();
+    this.getDialogTagDataList();
   }
 };
 </script>

+ 3 - 1
src/components/pages/classroomObservation/dialog/uploadFileToCreateClassDialog.vue

@@ -212,7 +212,8 @@ export default {
         name: _name,
         url: data.Location,
         type: _type,
-        size: size
+        size: size,
+        fileObj:this.fileList.find(i => i.index == res.index).fileObj,
       };
       this.fileList.find(i => i.index == res.index).status = "success";
       let uploadingFile = this.fileList.find(file => file.status === "wait");
@@ -247,6 +248,7 @@ export default {
               index: uuidv4(),
               successData: null,
               name: files[i].name,
+              fileObj:files[i],
               type: files[i].type,
               progress: { status: "", percent: 0, key: "", uploadid: "" },
               status: "wait"

+ 7 - 3
src/components/pages/classroomObservation/index.vue

@@ -83,10 +83,10 @@
       </div>
       <div class="co-h2-right">
 
-        <!--<div class="co-h2-r-btn" style="background: rgba(54, 129, 252, 1)" @click.stop="batchBtn()">
+        <div class="co-h2-r-btn" style="background: rgba(54, 129, 252, 1)" @click.stop="batchBtn()" v-show="false">
           <span class="co-h2-r-b-icon3"></span>
           <div style="color: #fff;">批量创建</div>
-        </div> -->
+        </div>
         <div
           :class="['co-h2-r-btn', fileId && tid ? '' : 'ca-h2-r-noActive']"
           style="background: rgba(54, 129, 252, 1)"
@@ -206,7 +206,7 @@
       ref="changeCourseNameDialogRef"
       @success="changeCourseSuccess"
     />
-    <batchCreationClassDialog ref="batchCreationClassDialogRef"/>
+    <batchCreationClassDialog ref="batchCreationClassDialogRef" @addNewCourseOption="addNewCourseOption"/>
 
     <!-- <addNewCourseDialog
 			:courseList="optionData"
@@ -388,6 +388,7 @@ export default {
           userid: this.userId,
           template: json
         };
+        console.log("创建新课堂",params);
         this.ajax
           .post(
             "https://gpt4.cocorobo.cn/insert_classroom_observation_template",
@@ -1214,6 +1215,9 @@ export default {
     batchBtn(){
       this.$refs.batchCreationClassDialogRef.open();
     },
+    addNewCourseOption(newOption){
+      this.optionData.unshift(newOption);
+    },
   },
   mounted() {
     this.getCourseList().then(_ => {

+ 170 - 24
src/components/pages/classroomObservation/newComponents/batchClassCard.vue

@@ -8,15 +8,15 @@
         <div class="bcc_r_t_left">
           <div class="bcc_r_t_l_image">
             <img
-              v-if="cardData.jsonData.fileData.type=='text/plain'"
+              v-if="cardData.jsonData.fileData.type == 'text/plain'"
               src="../../../../assets/icon/classroomObservation/file_icon.svg"
             />
             <img
-              v-if="cardData.jsonData.fileData.type=='audio/wav'"
+              v-if="cardData.jsonData.fileData.type == 'audio/wav'"
               src="../../../../assets/icon/classroomObservation/audio_file.svg"
             />
             <img
-              v-if="cardData.jsonData.fileData.type=='video/mp4'"
+              v-if="cardData.jsonData.fileData.type == 'video/mp4'"
               src="../../../../assets/icon/classroomObservation/videoFile_icon.svg"
             />
           </div>
@@ -24,6 +24,7 @@
             <div>
               <span>{{ cardData.jsonData.baseMessage.courseName }}</span>
               <img
+                v-show="!cardData.status=='2'"
                 src="../../../../assets/icon/classroomObservation/table_edit.svg"
                 @click="editBaseMessage"
               />
@@ -32,12 +33,54 @@
           </div>
         </div>
         <div class="bcc_r_t_right">
-          <span class="status_wait" v-if="cardData.status == '3'">停止中</span>
+          <span class="status_wait" v-if="cardData.status == '3'">待开始</span>
           <span class="status_fail" v-if="cardData.status == '4'">失败</span>
           <span class="status_success" v-if="cardData.status == '2'"
             >已完成</span
           >
-          <span class="status_doing" v-if="cardData.status == '1'">处理中</span>
+          <span class="status_doing" v-if="cardData.status == '1'">
+            <span>处理中</span>
+            <div class="stepBox">
+              <div
+                class="sb_item"
+                v-for="(item, index) in cardData.jsonData.steps"
+                :key="index"
+              >
+                <!-- 1完成 -->
+                <img
+                  src="../../../../assets/icon/classroomObservation/successStatus_icon.svg"
+                  v-if="item.status==='1'"
+                />
+
+
+                <!-- 0等待 -->
+                <img
+                  class="rotation"
+                  src="../../../../assets/icon/classroomObservation/waitStatus_icon.svg"
+                  v-if="item.status==='0'"
+                />
+
+
+                <!-- 2处理中 -->
+                <img
+                  class="rotation"
+                  src="../../../../assets/icon/classroomObservation/isDoStatus_icon.svg"
+                  v-if="item.status==='2'"
+                />
+                <div>{{ item.text }}</div>
+                <span>
+                  <span
+                    v-if="
+                      item.status == '2' &&
+                        item.progress &&
+                        item.progress !== '0'
+                    "
+                    >{{ item.progress }}%</span
+                  ></span
+                >
+              </div>
+            </div>
+          </span>
           <span class="status_wait" v-if="cardData.status == '0'"
             >等待处理</span
           >
@@ -45,21 +88,50 @@
       </div>
       <div class="bcc_r_bottom">
         <div class="bcc_r_b_left">
-          <el-input placeholder="备注" v-model="cardData.remarks" @change="changeRemarks"></el-input>
+          <el-input
+            placeholder="备注"
+            v-model="cardData.remarks"
+            @change="changeRemarks"
+          ></el-input>
         </div>
         <div class="bcc_r_b_right">
           <el-button
             size="small"
             @click="goToEdit"
-            v-if="[2].includes(cardData.status)"
+            v-if="['2'].includes(cardData.status)"
             >前往编辑</el-button
           >
-          <!-- <el-button size="small" @click="lookReport">查看报告</el-button> -->
           <el-button
+            size="small"
+            @click="lookReport"
+            v-if="['2'].includes(cardData.status)"
+            >查看报告</el-button
+          >
+          <!-- <el-button
             size="small"
             @click="regenerate"
-            v-if="[2, 3].includes(cardData.status)"
+            v-if="['2', '3'].includes(cardData.status)"
             >重新生成</el-button
+          > -->
+          <el-button
+            size="small"
+            type="primary"
+            v-if="['3', '0'].includes(cardData.status)"
+            @click="start"
+            >开始</el-button
+          >
+          <el-button
+            size="small"
+            v-if="['1'].includes(cardData.status)"
+            @click="pause"
+            >暂停</el-button
+          >
+          <el-button
+            size="small"
+            v-if="['0', '1'].includes(cardData.status)"
+            type="danger"
+            @click="stop"
+            >停止</el-button
           >
         </div>
       </div>
@@ -76,10 +148,10 @@ export default {
         return {};
       }
     },
-    isSelect:{
-      type:Boolean,
-      default:false
-    },
+    isSelect: {
+      type: Boolean,
+      default: false
+    }
   },
   data() {
     return {
@@ -152,7 +224,7 @@ export default {
     };
   },
   watch: {
-    isSelect(newValue){
+    isSelect(newValue) {
       this.checked = newValue;
     },
     // checked(newValue) {
@@ -167,10 +239,10 @@ export default {
       handler(newValue) {
         if (newValue) {
           this.cardData = JSON.parse(JSON.stringify(newValue));
-          this.$forceUpdate()
+          this.$forceUpdate();
         }
       }
-    },
+    }
     // cardData(newValue) {
     //   if (JSON.stringify(this.data) != JSON.stringify(newValue)) {
     //     this.$emit("changeData", {});
@@ -179,7 +251,7 @@ export default {
   },
   methods: {
     editBaseMessage() {
-      this.$emit("editBaseMessage",this.cardData.id);
+      this.$emit("editBaseMessage", this.cardData.id);
     },
     //前往编辑
     goToEdit() {
@@ -193,18 +265,27 @@ export default {
     regenerate() {
       this.$message.info("重新生成");
     },
-    changeRemarks(newValue){
-      if(this.data.remarks !=newValue){
-        this.$emit("changeData",{field:['remarks'],data:this.cardData})
+    changeRemarks(newValue) {
+      if (this.data.remarks != newValue) {
+        this.$emit("changeData", { field: ["remarks"], data: this.cardData });
       }
     },
-    changeChecked(newValue){
-      if (newValue && newValue!=this.isSelect) {
-        this.$emit("changeChecked", {type:0,id:this.cardData.id});
+    changeChecked(newValue) {
+      if (newValue && newValue != this.isSelect) {
+        this.$emit("changeChecked", { type: 0, id: this.cardData.id });
       } else {
-        this.$emit("changeChecked", {type:1,id:this.cardData.id});
+        this.$emit("changeChecked", { type: 1, id: this.cardData.id });
       }
     },
+    start() {
+      this.$emit("taskBtn", { type: "startTask", id: this.cardData.id });
+    },
+    stop() {
+      this.$emit("taskBtn", { type: "stopTask", id: this.cardData.id });
+    },
+    pause() {
+      this.$emit("taskBtn", { type: "pauseTask", id: this.cardData.id });
+    }
   },
   mounted() {
     if (this.data) {
@@ -281,6 +362,58 @@ export default {
 
 .bcc_r_t_right > span {
   font-size: 16px;
+  display: block;
+  position: relative;
+}
+
+.stepBox {
+  width: 200px;
+  height: auto;
+  background-color: #fff;
+  position: absolute;
+  left: calc(100% + 22px);
+  top: -21px;
+  box-sizing: border-box;
+  border: 1px solid #d9d9d9;
+  border-radius: 12px;
+  box-shadow: 0px 4px 10px 0px rgba(0, 0, 0, 0.05);
+  color: #000;
+  padding: 10px 10px;
+  cursor: default;
+}
+
+.sb_item {
+  width: 100%;
+  height: 25px;
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+}
+
+.sb_item > img {
+  width: 20px;
+  height: 20px;
+}
+
+.sb_item > div {
+  max-width: calc(100% - 20px - 35px - 15px);
+  width: calc(100% - 20px - 35px - 15px);
+  height: 100%;
+  margin: 0 5px 0 10px;
+  display: block;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  line-height: 24px;
+}
+
+.sb_item > span {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 35px;
+  height: 100%;
+  color: rgba(54, 129, 252, 1);
 }
 
 .bcc_r_t_l_message {
@@ -381,4 +514,17 @@ export default {
 .status_fail {
   color: rgb(185, 2, 2);
 }
+
+.rotation {
+  animation: rotate 4s linear infinite;
+}
+
+@keyframes rotate {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
 </style>

+ 1211 - 0
src/components/pages/classroomObservation/tools/mixin.js

@@ -0,0 +1,1211 @@
+import {
+  v4 as uuidv4
+} from 'uuid';
+
+const getFileBody = (url) => {
+  return new Promise((resolve, reject) => {
+    var credentials = {
+      accessKeyId: "AKIATLPEDU37QV5CHLMH",
+      secretAccessKey: "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",
+    }; //秘钥形式的登录上传
+    window.AWS.config.update(credentials);
+    window.AWS.config.region = "cn-northwest-1"; //设置区域
+    let url2 = url;
+    let _url2 = "";
+    if (
+      url2.indexOf("https://view.officeapps.live.com/op/view.aspx?src=") != -1
+    ) {
+      _url2 = url2.split(
+        "https://view.officeapps.live.com/op/view.aspx?src="
+      )[1];
+    } else {
+      _url2 = url2;
+    }
+    var s3 = new window.AWS.S3({ params: { Bucket: "ccrb" } });
+    let name = decodeURIComponent(_url2.split("https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/")[1])
+    var params = {
+      Bucket: "ccrb",
+      Key: name
+    };
+    s3.getObject(params, function (err, data) {
+      if (err) {
+        console.log(err, err.stack)
+        resolve({ data: 1 });
+      } else {
+        resolve({ data: data.Body });
+        console.log(data);
+      }          // sxuccessful response
+
+    });
+  });
+};
+
+var OpenCC = require("opencc-js");
+
+let converter = OpenCC.Converter({
+  from: "hk",
+  to: "cn"
+});
+
+import _ from "lodash";
+import Papa from "papaparse";
+import markdownIt from "markdown-it";
+
+export const toolMixin = {
+  methods: {
+    test() {
+      this.$message.info("测试")
+    },
+    getTextContentMixin(file) {
+      return new Promise((resolve) => {
+        const txtRegex = /\.(txt|csv)$/i;
+        if (txtRegex.test(file.url)) {
+          this.getFile(file.url).then(fileData => {
+            const arr = Papa.parse(fileData.data, {
+              header: false
+            }).data.slice(1);
+            // console.log(arr)
+            const _editorBarDataContent = `<table
+border="0"
+width="100%"
+cellpadding="0"
+cellspacing="0"
+style="text-align: center">
+<tbody>
+<tr>
+  <th>序号</th>
+  <th>开始时间</th>
+  <th>结束时间</th>
+  <th>发言内容</th>
+  <th>时长</th>
+  <th>说话人身份</th>
+  <th>行为编码</th>
+</tr>
+${arr.map(row => `<tr>
+  <td>${_.get(row, 0, "")}</td>
+  <td>${_.get(row, 1, "")}</td>
+  <td>${_.get(row, 2, "")}</td>
+  <td>${_.get(row, 3, "")}</td>
+  <td>${_.get(row, 4, "")}</td>
+  <td>${_.get(row, 5, "")}</td>
+  <td>${_.get(row, 6, "")}</td>
+</tr>
+`).join("\n")}</tbody></table>`;
+
+            var blob = new Blob([_editorBarDataContent], { type: "text/plain;charset=utf-8" });
+            blob.lastModifiedDate = new Date();
+            blob.name = `${file.name.replace('.txt', '')}_classroomObservation.txt`;
+            this.uploadFileMixin(blob).then(upload => {
+              resolve({ editorBarData: { type: "0", url: upload.Location, content: _editorBarDataContent } })
+              // this.ajax
+              //   .put("https://gpt4.cocorobo.cn/upload_file_knowledge", {
+              //     url: upload.Location
+              //   })
+              //   .then(res => {
+              //     let resData = res.data.FunctionResponse;
+              //     if (resData.result && resData.result.id) {
+              //       resolve({ fileId: resData.result.id, editorBarData: { type: "0", url: upload.Location }, })
+              //     }
+              //   })
+
+
+            })
+          })
+        }
+      })
+    },
+    uploadFileMixin(file) {
+      return new Promise((resolve, reject) => {
+        var credentials = {
+          accessKeyId: "AKIATLPEDU37QV5CHLMH",
+          secretAccessKey: "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"
+        }; //秘钥形式的登录上传
+        window.AWS.config.update(credentials);
+        window.AWS.config.region = "cn-northwest-1"; //设置区域
+
+        var bucket = new window.AWS.S3({ params: { Bucket: "ccrb" } }); //选择桶
+        var _this = this;
+
+        if (file) {
+          var params = {
+            Key:
+              file.name.split(".")[0] +
+              new Date().getTime() +
+              "." +
+              file.name.split(".")[file.name.split(".").length - 1],
+            ContentType: file.type,
+            Body: file,
+            "Access-Control-Allow-Credentials": "*",
+            ACL: "public-read"
+          }; //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置
+          var options = {
+            partSize: 2048 * 1024 * 1024,
+            queueSize: 2,
+            leavePartsOnError: true
+          };
+          bucket
+            .upload(params, options)
+            .on("httpUploadProgress", function (evt) {
+              //这里可以写进度条
+              // _this.progressData.value = parseInt((evt.loaded * 100) / evt.total);
+              // console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
+            })
+            .send(function (err, data) {
+              if (err) {
+                _this.$message.error("上传失败");
+              } else {
+                resolve(data);
+              }
+            });
+        }
+      })
+    },
+    getFileIdMixin(url) {
+      return new Promise((resolve) => {
+        this.ajax
+          .put("https://gpt4.cocorobo.cn/upload_file_knowledge", {
+            url: url
+          })
+          .then(res => {
+            let resData = res.data.FunctionResponse;
+            if (resData.result && resData.result.id) {
+              resolve({ fileId: resData.result.id })
+            }
+          })
+      })
+    },
+    getVideoToVoiceAndUploadMixin(fileData) {
+      return new Promise(async (resolve) => {
+        let _file = null;
+        console.log("fileData👉", fileData)
+        if (fileData.fileObj) {
+          _file = fileData.fileObj
+        } else if (fileData.url) {
+          let videoRes = await getFileBody(fileData.url);
+          if (videoRes.data === 1) return resolve({ data: 1 })
+          // 把uint8Array转换为视频文件
+          _file = new File([videoRes.data], 'video.mp4', { type: 'video/mp4' });
+        }
+        if (!_file) return resolve({ data: 0 })
+        console.log("需要处理的文件👉", _file)
+        try {
+          const reader = new FileReader();
+          reader.onload = async (e) => {
+            try {
+              // 创建音频上下文
+              const audioContext = new (window.AudioContext || window.webkitAudioContext)();
+
+              //解码音频数据
+              const buffer = await audioContext.decodeAudioData(e.target.result);
+
+              //创建离线音频上下文
+              const offlineAudioContext = new OfflineAudioContext({ numberOfChannels: buffer.numberOfChannels, length: buffer.length, sampleRate: buffer.sampleRate });
+
+              //创建音源节点
+              const source = offlineAudioContext.createBufferSource();
+              source.buffer = buffer;
+              source.connect(offlineAudioContext.destination);
+              source.start();
+
+              //渲染音频
+              const renderedBuffer = await offlineAudioContext.startRendering();
+              const wavBlob = this.bufferToWav(renderedBuffer);
+
+              // blob转成file文件
+              const audioFile = new File([wavBlob], 'audio.wav', { type: 'audio/wav' });
+              this.uploadFileMixin(audioFile).then(upload => {
+                resolve({ audioUrl: upload })
+              })
+            } catch (error) {
+              console.log("👉", error);
+              return resolve({ data: 2 })
+            }
+
+          }
+          reader.readAsArrayBuffer(_file);
+        } catch (error) {
+          console.log("👉", error);
+          return resolve({ data: 2 })
+        }
+      })
+    },
+    bufferToWav(audioBuffer) {
+      const numOfChan = audioBuffer.numberOfChannels;
+      const length = audioBuffer.length * numOfChan * 2;
+      const buffer = new ArrayBuffer(44 + length);
+      const view = new DataView(buffer);
+      const channels = [];
+      let pos = 0;
+      // 获取通道数据
+      for (let i = 0; i < audioBuffer.numberOfChannels; i++) {
+        channels.push(audioBuffer.getChannelData(i));
+      }
+      // 写入WAV头
+      this.writeUTFBytes(view, 0, 'RIFF');
+      view.setUint32(4, 44 + length - 8, true);
+      this.writeUTFBytes(view, 8, 'WAVE');
+      this.writeUTFBytes(view, 12, 'fmt ');
+      view.setUint32(16, 16, true);
+      view.setUint16(20, 1, true);
+      view.setUint16(22, numOfChan, true);
+      view.setUint32(24, audioBuffer.sampleRate, true);
+      view.setUint32(28, audioBuffer.sampleRate * 2 * numOfChan, true);
+      view.setUint16(32, numOfChan * 2, true);
+      view.setUint16(34, 16, true);
+      this.writeUTFBytes(view, 36, 'data');
+      view.setUint32(40, length, true);
+      // 写入PCM数据
+      pos = 44;
+      for (let i = 0; i < audioBuffer.length; i++) {
+        for (let j = 0; j < numOfChan; j++) {
+          const sample = Math.max(-1, Math.min(1, channels[j][i]));
+          view.setInt16(pos, sample < 0 ? sample * 0x8000 : sample * 0x7FFF, true);
+          pos += 2;
+        }
+      }
+      return new Blob([buffer], { type: 'audio/wav' });
+    },
+    writeUTFBytes(view, offset, string) {
+      for (let i = 0; i < string.length; i++) {
+        view.setUint8(offset + i, string.charCodeAt(i));
+      }
+    },
+    getFile(url) {
+      return new Promise((resolve, reject) => {
+        var credentials = {
+          accessKeyId: "AKIATLPEDU37QV5CHLMH",
+          secretAccessKey: "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"
+        }; //秘钥形式的登录上传
+        window.AWS.config.update(credentials);
+        window.AWS.config.region = "cn-northwest-1"; //设置区域
+        let url2 = url;
+        let _url2 = "";
+        if (
+          url2.indexOf("https://view.officeapps.live.com/op/view.aspx?src=") != -1
+        ) {
+          _url2 = url2.split(
+            "https://view.officeapps.live.com/op/view.aspx?src="
+          )[1];
+        } else {
+          _url2 = url2;
+        }
+        var s3 = new window.AWS.S3({ params: { Bucket: "ccrb" } });
+        let name = decodeURIComponent(
+          _url2.split("https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/")[1]
+        );
+        var params = {
+          Bucket: "ccrb",
+          Key: name
+        };
+        s3.getObject(params, function (err, data) {
+          if (err) {
+            console.log(err, err.stack);
+            resolve({ data: 1 });
+          } else {
+            const fileContent = data.Body.toString("utf-8");
+            resolve({ data: fileContent });
+          } // sxuccessful response
+        });
+        // axios({
+      });
+    },
+    getAnalysisMixin(obj) {
+      return new Promise(async (resolve) => {
+        let { fileId, assistantData, content, analysisData, baseMessage } = obj;
+        let type = 0;
+        let tips = ""
+        analysisData.mId = assistantData.id;
+        // console.log("处理数据👉", fileId, assistantData, content, analysisData, baseMessage)
+        if (['f8795150-699c-11ef-b873-005056b86db5', '01928d2b-699d-11ef-b873-005056b86db5', '069af7b9-699d-11ef-b873-005056b86db5', 'bfe844b1-7a45-11ef-9b30-005056b86db5'].includes(assistantData.id)) {//S-T分析:课堂时间分配   S-T分析:师生互动分析  S-T分析:教学模式分析 课堂活动光谱图
+          try {
+            let _result = [];
+            let _data = content;
+            let _div = document.createElement("div");
+            _div.innerHTML = _data;
+            let _tableRows = _div.querySelectorAll(`table tbody tr`);
+            _tableRows.forEach((i, index) => {
+              if (index == 0) return;
+              let obj = {
+                index: i.cells[0].textContent,
+                startTime: i.cells[1].textContent,
+                endTime: i.cells[2].textContent,
+                message: i.cells[3].textContent,
+                time: i.cells[4].textContent,
+                role: i.cells[5] ? i.cells[5].textContent : "",
+                behavior: i.cells[6] ? i.cells[6].textContent : ""
+              };
+              _result.push(obj);
+            });
+            if (_result.length == 0) return resolve({ data: 1, err: "未找到表格数据" });
+            if (assistantData.id == "f8795150-699c-11ef-b873-005056b86db5") {//课堂时间分配
+              let resultData = await this.getTimeAllocationDataMixin(_result, fileId)
+              if (resultData.message) analysisData.content = resultData.message;
+              if (resultData.eCharts) analysisData.eChartData = resultData.eCharts;
+              return resolve({ data: analysisData })
+            } else if (assistantData.id == "01928d2b-699d-11ef-b873-005056b86db5") {//师生互动分析
+              let resultData = await this.getInteractionAnalysisData(_result, fileId)
+              if (resultData.message) analysisData.content = resultData.message;
+              if (resultData.eCharts) analysisData.eChartData = resultData.eCharts;
+              return resolve({ data: analysisData })
+            } else if (assistantData.id == "069af7b9-699d-11ef-b873-005056b86db5") {//教学模式分析
+              let resultData = await this.getTeachingModeData(_result, fileId)
+              if (resultData.message) analysisData.content = resultData.message;
+              if (resultData.RT) analysisData.RT = resultData.RT;
+              if (resultData.CH) analysisData.CH = resultData.CH;
+              return resolve({ data: analysisData })
+            } else if (assistantData.id == "bfe844b1-7a45-11ef-9b30-005056b86db5") {//课堂活动光谱图
+              let resultData = await this.getSpectrogram(_result, fileId, content, assistantData)
+              if (resultData.message) analysisData.content = resultData.message;
+              if (resultData.eCharts) analysisData.getSpectrogram = resultData.eCharts;
+              return resolve({ data: analysisData })
+            }
+          } catch (error) {
+            return resolve({ data: 1, err: err })
+          }
+        } else {
+          let _msg = `使用文件检索的方式完整的去分析文件内容,并请完全按照要求输出。`;
+          if (assistantData.tips) {
+            tips = assistantData.tips;
+            type = 1;
+          } else if (assistantData.agentid) {
+            type = 0;
+          }
+          if (assistantData.id === '6b4a9650-48be-11ef-936b-12e77c4cb76b') {
+            _msg = `使用文件检索的方式完整的去分析文件内容,并基于以下的课堂基本内容,使用cpote课程设计模型改编一堂同主题的课程。
+课堂名称:${baseMessage.courseName}  搜课年级:${baseMessage.grade}  授课科目:${baseMessage.subject}`;
+          }
+          let params = {
+            id:
+              type == 0
+                ? assistantData.agentid
+                : "f8e1ebb2-2e0d-11ef-8bf4-12e77c4cb76b",
+            message: type == 0 ? _msg : tips,
+            session_name: uuidv4(),
+            userId: this.userId,
+            file_ids: fileId ? [fileId] : [],
+            model: "gpt-4o-2024-11-20",
+            sound_url: "",
+            temperature: 0.2,
+            top_p: 1,
+            max_completion_tokens: 4096,
+            stream: false,
+            uid: uuidv4()
+          };
+          this.ajax
+            .post("https://appapi.cocorobo.cn/api/agentchats/ai_agent_chat", params)
+            .then(async res => {
+              let _data = res.data;
+              analysisData.content = _data.message;
+              if (['1', '2', '3'].includes(assistantData.echartsType)) {
+                let echartsData = await this.getEChartsDataMixin(assistantData.echartsType, _data.message)
+                if (echartsData.data == 1) {
+                  console.log(`生成表格失败${echartsData.err}`)
+                  resolve({ data: analysisData })
+                } else {
+                  analysisData.eChartData = echartsData.data;
+                  resolve({ data: analysisData })
+                }
+              } else {
+                resolve({ data: analysisData })
+              }
+            })
+            .catch(err => {
+              resolve({ data: 1, err: err })
+            });
+        }
+      })
+    },
+    getContentTableMixin(content) {
+      return new Promise(resolve => {
+        let _content = content;
+        const md = new markdownIt();
+        let _contentHtml = md.render(_content);
+
+        let _contentTableList = [];
+        const rowRegex = /<tr[^>]*>([\s\S]*?)<\/tr>/g; // 匹配表格行,[\s\S] 匹配所有字符
+        const cellRegex = /<(th|td)[^>]*>([\s\S]*?)<\/\1>/g; // 匹配单元格,[\s\S] 匹配所有字符
+
+        let rowMatch;
+        while ((rowMatch = rowRegex.exec(_contentHtml)) !== null) {
+          const rowContent = rowMatch[1]; // 每一行的内容
+          const rowData = [];
+          let cellMatch;
+
+          // 匹配每个单元格 (th 或 td)
+          while ((cellMatch = cellRegex.exec(rowContent)) !== null) {
+            let _text = cellMatch[2].trim();
+            _text = _text.replace(/&[a-zA-Z]+;/g, "");
+            _text = _text.replace(/<\/?[^>]+(>|$)/g, "");
+            rowData.push(_text); // 将每个单元格的内容添加到当前行的数组中
+          }
+
+          // 如果该行有数据,推送到 _contentTableList 中
+          if (rowData.length) {
+            _contentTableList.push(rowData);
+          }
+        }
+
+        // 输出提取的表格数据
+
+        resolve(_contentTableList);
+      });
+    },
+    getEChartsDataMixin(type = "1", content) {
+      return new Promise((resolve) => {
+        if (type === "1") {
+          //词云图
+          return this.getContentTableMixin(content).then(res => {
+            try {
+              if (res.length <= 0) {
+                return resolve({ data: 0 })//未找到表格数据
+              }
+
+              let _result = [];
+              res.forEach((item, index) => {
+                if (index == 0) return; //去掉表头
+                let _valueItem = item[2] ? item[2] : item[1];
+                let _value = _valueItem.match(/(\d+)/);
+                _value = _value ? parseInt(_value[0]) : 0;
+                _result.push({
+                  value: _value,
+                  name: item[0],
+                  textStyle: { color: this.getRandomColorMixin() }
+                });
+              });
+
+              let _option = {
+                tooltip: {
+                  show: false
+                },
+                series: [
+                  {
+                    type: "wordCloud",
+                    sizeRange: [14, 38],
+                    rotationRange: [0, 0],
+                    keepAspect: false,
+                    shape: "circle",
+                    left: "center",
+                    top: "center",
+                    right: null,
+                    bottom: null,
+                    width: "100%",
+                    height: "100%",
+                    // rotationRange: [-90, 90],
+                    rotationStep: 20,
+                    data: _result
+                  }
+                ]
+              };
+              resolve({ data: _option })
+            } catch (e) {
+              resolve({ data: 1, err: e })
+            }
+          });
+        } else if (type == "2") {
+          //雷达图
+          //雷达图
+          return this.getContentTableMixin(content).then(res => {
+            try {
+              if (res.length <= 0) {
+                return resolve({ data: 0 })//未找到表格数据
+              }
+
+              let radarData = [];
+              let seriesData = { value: [] };
+
+              res.forEach((item, index) => {
+                if (index == 0) return; //去掉表头
+                radarData.push({ name: item[0], max: 5 });
+                let _valueItem = item[1] ? item[1] : "0";
+                let _value = _valueItem.match(/(\d+)/);
+                _value = _value ? parseInt(_value[0]) : 0;
+                seriesData.value.push(_value);
+              });
+
+              let _option = {
+                legend: {
+                  textStyle: {
+                    color: '#000'
+                  }
+                },
+                radar: {
+                  // shape: 'circle',
+                  indicator: radarData,
+                  name: {
+                    textStyle: {
+                      color: '#000'
+                    }
+                  }
+                },
+                series: [
+                  {
+                    type: "radar",
+                    data: [seriesData],
+                    label: {
+                      color: '#000'
+                    }
+                  }
+                ]
+              };
+              resolve({ data: _option })
+            } catch (e) {
+              resolve({ data: 1, err: e })
+            }
+          });
+        } else if (type == "3") {
+          //能量柱图
+          return this.getContentTableMixin(content).then(res => {
+            try {
+              if (res.length <= 0) {
+                return resolve({ data: 0 })//未找到表格数据
+              }
+
+              let _data = [];
+              let stepList = [];
+              stepList = this.calculateTopValuesMixin(res.length - 1);
+              res.forEach((item, index) => {
+                if (index == 0) return;
+                let _valueItem = item[1] ? item[1] : "0";
+                let _value = _valueItem.match(/(\d+)/);
+                _value = _value ? parseInt(_value[0]) : 0;
+                // 求百分比
+                _value = Math.floor((_value / 5).toFixed(2) * 100);
+                _data.push({
+                  value: _value,
+                  name: item[0],
+                  title: { offsetCenter: ["0%", `${stepList[index - 1]}%`] },
+                  detail: {
+                    valueAnimation: true,
+                    offsetCenter: ["0%", `${stepList[index - 1] + 15}%`]
+                  }
+                });
+              });
+
+              let _option = {
+                series: [
+                  {
+                    type: "gauge",
+                    startAngle: 90,
+                    endAngle: -270,
+                    pointer: {
+                      show: false
+                    },
+                    progress: {
+                      show: true,
+                      overlap: false,
+                      roundCap: true,
+                      clip: false,
+                      itemStyle: {
+                        borderWidth: 1,
+                        borderColor: "#464646"
+                      }
+                    },
+                    axisLine: {
+                      lineStyle: {
+                        width: 40
+                      }
+                    },
+                    splitLine: {
+                      show: false,
+                      distance: 0,
+                      length: 10
+                    },
+                    axisTick: {
+                      show: false
+                    },
+                    axisLabel: {
+                      show: false,
+                      distance: 50
+                    },
+                    data: _data,
+                    title: {
+                      fontSize: 14,
+                      color: "#000"
+                    },
+                    detail: {
+                      width: 50,
+                      height: 14,
+                      fontSize: 14,
+                      color: "#000",
+                      borderColor: "inherit",
+                      borderRadius: 20,
+                      borderWidth: 1,
+                      formatter: "{value}%"
+                    }
+                  }
+                ]
+              };
+              resolve({ data: _option })
+            } catch (e) {
+              resolve({ data: 1, err: e })
+            }
+          });
+        }
+      })
+    },
+    getRandomColorMixin() {
+      // 生成一个随机的 0 到 255 的数字,并将其转换为两位的十六进制形式
+      const randomHex = () => {
+        const hex = Math.floor(Math.random() * 256).toString(16);
+        return hex.length === 1 ? "0" + hex : hex; // 确保每个部分有两位
+      };
+
+      // 组合三种颜色(红、绿、蓝)的随机值
+      return `#${randomHex()}${randomHex()}${randomHex()}`;
+    },
+    calculateTopValuesMixin(len, minTop = -80, maxTop = 70, maxStep = 40) {
+      const length = len;
+      const middleIndex = Math.floor(length / 2); // 中间位置的索引
+      const totalRange = maxTop - minTop;
+      let step = totalRange / (length - 1); // 默认间隔
+
+      // 如果间隔大于 10%,则设置为最大 10%
+      if (step > maxStep) {
+        step = maxStep;
+      }
+
+      const topValues = [];
+
+      // 计算中间元素的top值
+      const middleTop = (minTop + maxTop) / 2;
+      topValues[middleIndex] = middleTop;
+
+      // 从中间向两边扩展,确保每个元素的top值
+      for (let i = middleIndex - 1; i >= 0; i--) {
+        topValues[i] = topValues[i + 1] - step; // 向上扩展
+      }
+
+      for (let i = middleIndex + 1; i < length; i++) {
+        topValues[i] = topValues[i - 1] + step; // 向下扩展
+      }
+
+      return topValues;
+    },
+    // 课堂时间分配
+    getTimeAllocationDataMixin(_dataList, fileId) {
+      return new Promise(async (resolve) => {
+        let _data = _dataList.reduce(
+          (pre, cur) => {
+            if (cur.role == "学生") {
+              pre[1].value += this.convertToSeconds(cur.time);
+            } else if (cur.role == "老师") {
+              pre[0].value += this.convertToSeconds(cur.time);
+            }
+            return pre;
+          },
+          [
+            { value: 0, name: "老师" },
+            { value: 0, name: "学生" }
+          ]
+        );
+        let _dataPercentage = JSON.parse(JSON.stringify(_data));
+        _data.forEach((i, index) => {
+          _dataPercentage[index].percentage =
+            (
+              (i.value / _data.reduce((pre, cur) => pre + cur.value, 0)) *
+              100
+            ).toFixed(2) + "%";
+        });
+
+        const _option = {
+          tooltip: {
+            left: "center",
+            trigger: "item",
+            formatter: "{a} <br/>{b}: {d}%",
+            textStyle: {
+              color: '#000000'
+            }
+          },
+          legend: {
+            top: "5%",
+            left: "center",
+            textStyle: {
+              color: '#000000'
+            }
+          },
+          series: [
+            {
+              name: "课堂时间分配",
+              type: "pie",
+              radius: ["40%", "70%"],
+              label: {
+                formatter: "{b}: {d}%",
+                color: '#000000'
+              },
+              emphasis: {
+                label: {
+                  show: true,
+                  formatter: "{b}: {d}%",
+                  color: '#000000'
+                },
+                itemStyle: {
+                  shadowBlur: 10,
+                  shadowOffsetX: 0,
+                  shadowColor: "rgba(0, 0, 0, 0.5)"
+                }
+              },
+              data: _data
+            }
+          ]
+        };
+
+        let _msg = `这是某一节课的师生时间占比,请你分析,写出结论,并给出指导建议。请使用3句完整的话,分析并给出建议。 请注意,当老师或学生的时间占比在【40~59%】之间的时候,也认为师生占比约为1:1,各占50%,师生时间占比比较均衡。
+  师生时间占比数据:
+  老师占比:${_dataPercentage[0].percentage}
+  学生占比:${_dataPercentage[1].percentage}
+  `;
+        let message = await this.getAiContentMixin({ _msg: _msg, fileId: fileId })
+
+        if (message.data == 1) {
+          return resolve({ eCharts: _option, message: "" })
+        } else {
+          return resolve({ eCharts: _option, message: message.data })
+        }
+
+      })
+      // return this.getAiContent(_msg);
+    },
+    // 师生互动分析
+    getInteractionAnalysisData(_dataList, fileId) {
+      return new Promise(async (resolve) => {
+        let _pushData = [0, 0];
+        let _result = [];
+        _dataList.forEach(i => {
+          if (i.role == "老师") {
+            _pushData[0] += this.convertToSeconds(i.time);
+          } else if (i.role == "学生") {
+            _pushData[1] += this.convertToSeconds(i.time);
+          }
+          return _result.push(JSON.parse(JSON.stringify(_pushData)));
+        });
+        let _flatArray = _result.flat();
+        const _max = Math.max(..._flatArray);
+        const _maxValue = Math.ceil(_max / 100) * 100;
+
+        const _option = {
+          xAxis: {
+            name: "老师", // X轴标题
+            nameLocation: "end", // 标题位置
+            scale: true,
+            min: 0,
+            max: _maxValue,
+            axisLabel: {
+              color: "#000" // 设置字体颜色为#000
+            },
+            nameTextStyle: {
+              color: "#000" // 设置老师字体颜色为#000
+            }
+          },
+          yAxis: {
+            name: "学生", // Y轴标题
+            nameLocation: "end", // 标题位置
+            scale: true,
+            min: 0,
+            max: _maxValue,
+            axisLabel: {
+              color: "#000" // 设置字体颜色为#000
+            },
+            nameTextStyle: {
+              color: "#000" // 设置学生字体颜色为#000
+            }
+          },
+          grid: {
+            containLabel: true
+          },
+          series: [
+            {
+              name: "数据",
+              step: "start",
+              data: _result,
+              type: "line",
+              lineStyle: {
+              }
+            },
+            {
+              name: "对角线",
+              type: "line",
+              data: [
+                [0, 0],
+                [_maxValue, _maxValue]
+              ],
+              lineStyle: {
+                type: "dashed",
+              },
+              markLine: {
+                symbol: ["none", "none"]
+              }
+            }
+          ]
+        };
+
+        let _msg = `
+  ## 任务
+  请你结合 FIAS 相关的知识,根据以下提供给你的课堂原始数据(包含S和t的数据),请你具体描述整个课堂S行为与T行为的持续性与变化性。比如,课堂一开始老师占比主导,大约5分钟之后,进入到学生为主的小组讨论环节。在整个课堂之中,老师与学生的互动比较频繁,老师会频繁询问学生问题,引导学生思考。之后是授课时间与问答时间。等等。
+  ## 输出要求 请使用自然语言进行描述,使用不超过5句完整的话进行整体性、概括性的描述,不要包含具体的时长信息。总结性概括之后,使用1句话对整个课堂的教师引导行为进行鼓励和评价,再使用1句话给出相应的优化建议。
+  ## 你的知识库 定义与目的:S-T图,即学生-教师(Student-Teacher)图,主要用于记录和分析课堂上的学生行为(S)与教师行为(T)的时间分布。这种图形能够帮助教育专家和教师可视化课堂互动的流程,从而判断课堂的教学型态,如练习型、对话型、讲授型或混合型。 绘制方法:S-T图的绘制开始于教学的起始时刻,纵轴表示学生行为(S),横轴表示教师行为(T)。实际课堂观察或录像回放中,按照固定时间间隔(通常每30秒)采样,将对应的行为按时间顺序标记在相应的轴上。通过这种方法,可以清晰看到课堂上教师行为与学生行为的交替模式及其随时间的变化。 应用场景:例如,一个典型的应用是在分析不同类型课堂活动时使用S-T图。在讲授型课堂中,教师行为的时间占比会较高,S-T图显示较长的横轴(T行为)延续;而在练习型或对话型课堂中,学生行为的时间占比增高,显示为较长的纵轴(S行为)。
+  ## 课堂实录
+  ${JSON.stringify(_dataList)}
+  `;
+
+        let message = await this.getAiContentMixin({ _msg: _msg, fileId: fileId })
+
+        if (message.data == 1) {
+          return resolve({ eCharts: _option, message: "" })
+        } else {
+          return resolve({ eCharts: _option, message: message.data })
+        }
+      })
+
+    },
+    // 教学模式分析
+    getTeachingModeData(_dataList, fileId) {
+      return new Promise(async (resolve) => {
+        let _continuousTime = 0;
+        let _totalTime = 0;
+        let _continuousRole = "老师";
+        let _teacherTime = 0;
+        _dataList.forEach((item, index) => {
+          if (index == 0) {
+            //第一个
+            _continuousRole = item.role;
+          } else if (_dataList.length - 1 == index) {
+            //最后一个
+            if (_continuousRole == item.role) {
+              //连续对话了
+              _continuousTime += this.convertToSeconds(_dataList[index - 1].time);
+              _continuousTime += this.convertToSeconds(item.time);
+            } else {
+              //没连续对话
+              if (index >= 2) {
+                if (_dataList[index - 2].role == _dataList[index - 1].role) {
+                  _continuousTime += this.convertToSeconds(
+                    _dataList[index - 1].time
+                  );
+                } else {
+                  _continuousRole = item.role;
+                }
+              } else {
+                _continuousRole = item.role;
+              }
+            }
+          } else {
+            if (_continuousRole == item.role) {
+              //连续对话了
+              _continuousTime += this.convertToSeconds(_dataList[index - 1].time);
+            } else {
+              //没连续对话
+              if (index >= 2) {
+                if (_dataList[index - 2].role == _dataList[index - 1].role) {
+                  _continuousTime += this.convertToSeconds(
+                    _dataList[index - 1].time
+                  );
+                } else {
+                  _continuousRole = item.role;
+                }
+              } else {
+                _continuousRole = item.role;
+              }
+            }
+          }
+
+          if (item.role == "老师") {
+            _teacherTime += this.convertToSeconds(item.time);
+          }
+
+          _totalTime += this.convertToSeconds(item.time);
+        });
+
+        let _RT = (_teacherTime / _totalTime).toFixed(2);
+        let _CH = (_continuousTime / _totalTime).toFixed(2);
+
+        let _msg = `## 任务
+根据FIAS(弗兰德斯互动分析系统)理论,计算获得某一节课的RT和CH值。请你结合FIAS相关知识进行分析,使用3句完整的话对整个课堂进行分析,需注意包含这些内容:分析该课堂所属的教学模型,描述课堂的整体表现与特征,肯定老师做出的努力,以及给出相应的建议。
+## 你的知识
+根据RT和CH的值,教学模式通常被分为以下几种类型: 练习型:RT ≤ 0.3,表示学生行为占主导,教师行为较少。 讲授型:RT ≥ 0.7,表示教师行为占主导,学生参与较少。 对话型:CH ≥ 0.4,表示师生之间有较多的互动和转换。 混合型:0.3 < RT < 0.7,CH < 0.4,表示教学中既有教师讲授也有学生参与,但两者都不占绝对优势。
+## 数据
+RT:${_RT}
+CH:${_CH}
+`;
+        let message = await this.getAiContentMixin({ _msg: _msg, fileId: fileId })
+
+        if (message.data == 1) {
+          return resolve({ RT: _RT, CH: _CH, message: "" })
+        } else {
+          return resolve({ RT: _RT, CH: _CH, message: message.data })
+        }
+      })
+    },
+    //光谱图
+    getSpectrogram(_dataList, fileId, content, assistant) {
+      return new Promise(async (resolve) => {
+        try {
+          this.getContentTableMixin(content).then(async res => {
+            if (res.length <= 0) {
+              resolve({ data: 1, err: "无表格数据" })
+            }
+            let _tableData = res;
+            let _delIndex = _tableData.findIndex(i => i.includes("时间点"))
+            _tableData = _tableData.slice(_delIndex + 1)
+            let _result = [];
+            let identity = "老师"; //0:老师 1:学生
+            let startTime = "";
+            let endTime = "";
+            let sumTime = 0;
+            let upTime = '00:00:00';
+
+            _dataList.forEach((item, index) => {
+              if (index == 0) {
+                //第一个
+                identity = item.role;
+                startTime = item.startTime;
+                endTime = item.endTime;
+                sumTime = (this.convertToSeconds(item.endTime) - this.convertToSeconds(upTime));
+                upTime = item.endTime
+                // console.log(item.endTime,item.startTime,(this.convertToSeconds(item.endTime) - this.convertToSeconds(item.startTime)))
+                return;
+              }
+              if (item.role == identity) {
+                //没更换角色
+                sumTime += (this.convertToSeconds(item.endTime) - this.convertToSeconds(upTime));
+                endTime = item.endTime;
+                upTime = item.endTime
+                // console.log(item.endTime,item.startTime,(this.convertToSeconds(item.endTime) - this.convertToSeconds(item.startTime)))
+              } else {
+                //更换角色了
+                _result.push({
+                  startTime: startTime,
+                  endTime: endTime,
+                  identity: identity,
+                  sumTime: sumTime
+                });
+                identity = item.role;
+                startTime = item.startTime;
+                endTime = item.endTime;
+                sumTime = (this.convertToSeconds(item.endTime) - this.convertToSeconds(upTime));
+                upTime = item.endTime
+                // console.log(item.endTime,item.startTime,(this.convertToSeconds(item.endTime) - this.convertToSeconds(item.startTime)))
+              }
+              if (index == _dataList.length - 1) {
+                // console.log("👉???",this.convertToSeconds(item.endTime))
+              }
+            });
+
+            let breakpoint = [];
+
+            breakpoint = _tableData.map(i => this.convertToSeconds(i[0]))
+
+            _result = _result.filter(
+              i =>
+                i.identity == "老师" ||
+                i.identity == "学生"
+            );
+            // let
+            let _data = {
+              data: [],
+              breakpoint: []
+            };
+
+            _data.data = _result.map(i => ({ role: i.identity, type: i.identity == "老师" ? 0 : 1, value: i.sumTime }));
+            _data.breakpoint = breakpoint;
+
+            let _msg = assistant.tips;
+
+            let message = await this.getAiContentMixin({ _msg: _msg, fileId: fileId })
+
+            if (message.data == 1) {
+              return resolve({ eCharts: _data, message: "" })
+            } else {
+              return resolve({ eCharts: _data, message: message.data })
+            }
+            // spectrogramData  _data
+
+
+          });
+        } catch (e) {
+          return resolve({ data: 1, err: e })
+        }
+      })
+
+    },
+    getAiContentMixin(obj) {
+      return new Promise((resolve) => {
+        let { _msg, fileId } = obj
+        let parm = {
+          id: "f8e1ebb2-2e0d-11ef-8bf4-12e77c4cb76b",
+          message: _msg,
+          session_name: uuidv4(),
+          userId: this.userId,
+          file_ids: fileId ? [fileId] : [],
+          model: "gpt-4o-2024-11-20",
+          sound_url: "",
+          temperature: 0.2,
+          top_p: 1,
+          max_completion_tokens: 4096,
+          stream: false,
+          uid: uuidv4()
+        };
+
+        this.ajax
+          .post("https://appapi.cocorobo.cn/api/agentchats/ai_agent_chat", parm)
+          .then(res => {
+            let _data = res.data;
+            resolve({ data: _data.message })
+          })
+          .catch(err => {
+            resolve({ data: 1, err: err })
+          });
+      })
+    },
+    convertToSeconds(time) {
+      let parts = time.split(":");
+      let seconds = +parts[0] * 3600 + +parts[1] * 60 + +parts[2];
+      return seconds;
+    },
+    //创建课堂
+    createClassMixin(data) {
+      return new Promise((resolve) => {
+        let _analysisList = data.analysisList;
+        let createJson = _analysisList.map(i => {
+          return {
+            jsonData: i.jsonData,
+            type: i.Type,
+            index: i.tIndex
+          }
+        })
+        createJson = createJson.filter(i => !i.isOtherData && converter(i.jsonData.name) != converter("词频词汇分析"));
+        let params = {
+          tid: uuidv4(),
+          userid: this.userId,
+          template: createJson
+        }
+        this.ajax
+          .post(
+            "https://gpt4.cocorobo.cn/insert_classroom_observation_template",
+            params
+          )
+          .then(res => {
+            let _data = res.data.FunctionResponse;
+            if (converter(_data.message) == converter("创建成功")) {
+              this.ajax
+                .post("https://gpt4.cocorobo.cn/insert_classroom_observation", {
+                  tid: params.tid,
+                  type: 10,
+                  index: 0,
+                  json_data: JSON.stringify({ file_ids: data.file_ids }),
+                  userid: this.userId
+                })
+                .then(res2 => {
+                  let _data2 = res2.data.FunctionResponse;
+                  if (converter(_data2.message) == converter("创建成功")) {
+                    let newOption = { id: uuidv4(), label: data.baseMessage.courseName, value: params.tid }
+                    let params2 = {
+                      tid: params.tid,
+                      type: 0,
+                    }
+                    this.ajax
+                      .post(
+                        "https://gpt4.cocorobo.cn/get_classroom_observation_new",
+                        params2
+                      ).then(res3 => {
+                        let _data = res3.data.FunctionResponse.result.length
+                          ? JSON.parse(res3.data.FunctionResponse.result)
+                          : [];
+
+                        //替换基础信息
+                        let _bmData = _data.find(i => i.tIndex == 0);
+                        let _imageList = _data.find(i => i.tIndex == 1);
+                        _imageList.jsonData = data.baseMessage.imageList
+
+                        if (_bmData) {
+                          _bmData.jsonData = data.baseMessage;
+                          delete _bmData.jsonData.imageList;
+                          if (data.tagList) {
+                            _bmData.jsonData.dialogTagList = data.tagList;
+                          } else {
+                            _bmData.jsonData.dialogTagList = [
+                              { value: 0, name: "通用课堂分析", loading: false },
+                              { value: 1, name: "学科课堂分析", loading: false },
+                              { value: 2, name: "扩展分析", loading: false }
+                            ]
+                          }
+                        }
+
+                        let arr = [{ id: _bmData.id, jsonData: JSON.stringify(_bmData.jsonData) }, { id: _imageList.id, jsonData: JSON.stringify(_imageList.jsonData) }]
+                        let promises = [];
+                        arr.forEach(i => {
+                          promises.push(new Promise((resolve) => {
+                            this.ajax
+                              .post("https://gpt4.cocorobo.cn/update_classroom_observation", {
+                                id: i.id,
+                                json_data: i.jsonData
+                              })
+                              .then(res => {
+                                resolve();
+                              })
+                              .catch(e => {
+                                console.log("保存失败", e);
+                                resolve()
+                              });
+                          }))
+                        })
+                        Promise.all(promises).then(res=>{
+                          resolve({ data: newOption,tid:params.tid })
+                        })
+
+                        // promises.push(new Promise((resolve)=>{
+                        //    this.ajax
+                        //   .post("https://gpt4.cocorobo.cn/update_classroom_observation", {
+                        //     id: _bmData.id,
+                        //     json_data: JSON.stringify(_bmData.jsonData)
+                        //   })
+                        //   .then(res => {
+                        //     resolve({ data: newOption,tid:params.tid })
+                        //   })
+                        //   .catch(e => {
+                        //     console.log("保存失败", e);
+                        //     resolve({ data: newOption,tid:params.tid })
+                        //   });
+                        // }))
+
+
+                        // Promise.all(()=>{
+
+                        // },)
+                        // promises.push(new Promise((resolve)=>{
+
+                        // }))
+
+
+                        // 保存修改信息
+                        // this.ajax
+                        //   .post("https://gpt4.cocorobo.cn/update_classroom_observation", {
+                        //     id: _bmData.id,
+                        //     json_data: JSON.stringify(_bmData.jsonData)
+                        //   })
+                        //   .then(res => {
+                        //     resolve({ data: newOption,tid:params.tid })
+                        //   })
+                        //   .catch(e => {
+                        //     console.log("保存失败", e);
+                        //     resolve({ data: newOption,tid:params.tid })
+                        //   });
+                      }).catch(err => {
+                        console.log("修改基础信息失败", err)
+                        resolve({ data: 1, err: err })
+                      })
+                  }
+                }).catch(err => {
+                  resolve({ data: 1, err: err })
+                  console.log("存储fileId失败")
+                  console.log(err)
+                })
+            }
+          }).catch(err => {
+            resolve({ data: 1, err: err })
+            console.log("创建课堂失败")
+            console.log(err)
+          })
+      })
+    },
+  }
+};

+ 27 - 6
src/components/pages/sassPlatform/index.vue

@@ -47,6 +47,7 @@ export default {
       oid: this.$route.query.oid,
       org: this.$route.query.org,
       role:this.$route.query.role,
+      userData:null
     }
   },
   computed:{
@@ -85,11 +86,12 @@ export default {
             i.open = false;
 
             if(i.id=='e18d88b3-e828-11ef-b508-005056924926'){
-              _children.push(...[
-                {name:"年度考核",type:"annualAssessment",navIndex:`${index}-1`},
-                {name:"考核数据可视化",type:"evaluationDataVisualization",navIndex:`${index}-2`},
-                ]
-              )
+
+
+              if(this.userData && this.userData.type === 1 && this.userData.role == 1){
+                _children.push({name:"年度考核",type:"annualAssessment",navIndex:`${index}-1`})
+              }
+              _children.push({name:"考核数据可视化",type:"evaluationDataVisualization",navIndex:`${index}-2`})
               i.open = true;
             }
             i.children = _children;
@@ -123,14 +125,33 @@ export default {
           "&back=sass"
       );
     },
+    getUser(uid) {
+      return new Promise(resolve => {
+        let params = { uid: uid };
+        this.ajax
+          .get(this.$store.state.api + "getUser", params)
+          .then(res => {
+            let data = res.data[0][0];
+            this.userData = data;
+            resolve()
+          })
+          .catch(err => {
+            console.error(err);
+            resolve()
+          });
+      });
+    },
   },
   mounted(){
-    this.getNavType().then(_=>{
+    this.getUser(this.userId).then(()=>{
+      this.getNavType().then(_=>{
       if(this.navList.length>0){
         let _data = this.navList[0].children[0];
         this.changeNavIndex(_data.navIndex,_data)
       }
     });
+    })
+
   }
 }
 </script>

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.