瀏覽代碼

Merge branch 'beta' of https://git.cocorobo.cn/CocoRoboLabs/pbl-teacher-table into beta

lsc 2 月之前
父節點
當前提交
aac61e23a4
共有 27 個文件被更改,包括 3019 次插入332 次删除
  1. 1 1
      dist/index.html
  2. 0 0
      dist/static/css/app.ffcdb52d5346d2ed315e071c23bd299d.css
  3. 0 0
      dist/static/css/app.ffcdb52d5346d2ed315e071c23bd299d.css.map
  4. 0 0
      dist/static/js/0.4f3b05586c3acc102a54.js
  5. 0 0
      dist/static/js/0.4f3b05586c3acc102a54.js.map
  6. 0 0
      dist/static/js/0.df8814bab917ab2583e0.js.map
  7. 0 0
      dist/static/js/app.ac8a5fa38b4d662cb478.js
  8. 0 0
      dist/static/js/app.ac8a5fa38b4d662cb478.js.map
  9. 2 2
      dist/static/js/manifest.161e82026ac2ae03ab6f.js
  10. 0 0
      dist/static/js/manifest.161e82026ac2ae03ab6f.js.map
  11. 4 0
      src/assets/icon/classroomObservation/isDoStatus_icon.svg
  12. 10 0
      src/assets/icon/classroomObservation/successStatus_icon.svg
  13. 4 0
      src/assets/icon/classroomObservation/waitStatus_icon.svg
  14. 80 7
      src/components/pages/classroomObservation/components/analysisItem.vue
  15. 203 160
      src/components/pages/classroomObservation/components/analysisSpecialItem.vue
  16. 5 3
      src/components/pages/classroomObservation/components/resourceLibraryDialog.vue
  17. 684 53
      src/components/pages/classroomObservation/dialog/batchCreationClassDialog.vue
  18. 141 48
      src/components/pages/classroomObservation/dialog/editBaseMessageDialog.vue
  19. 122 15
      src/components/pages/classroomObservation/dialog/uploadFileToCreateClassDialog.vue
  20. 8 3
      src/components/pages/classroomObservation/index.vue
  21. 199 29
      src/components/pages/classroomObservation/newComponents/batchClassCard.vue
  22. 1476 0
      src/components/pages/classroomObservation/tools/mixin.js
  23. 21 1
      src/components/pages/components/appDialog.vue
  24. 2 1
      src/components/pages/newCourse/addCourse.vue
  25. 27 6
      src/components/pages/sassPlatform/index.vue
  26. 29 2
      src/components/pages/test/check/index.vue
  27. 1 1
      src/components/pages/testPerson/examine/index.vue

+ 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.ffcdb52d5346d2ed315e071c23bd299d.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.ac8a5fa38b4d662cb478.js></script></body></html><script>function stopSafari() {
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

File diff suppressed because it is too large
+ 0 - 0
dist/static/css/app.ffcdb52d5346d2ed315e071c23bd299d.css


File diff suppressed because it is too large
+ 0 - 0
dist/static/css/app.ffcdb52d5346d2ed315e071c23bd299d.css.map


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/0.4f3b05586c3acc102a54.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/0.4f3b05586c3acc102a54.js.map


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/0.df8814bab917ab2583e0.js.map


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/app.ac8a5fa38b4d662cb478.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/app.ac8a5fa38b4d662cb478.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

File diff suppressed because it is too large
+ 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>

+ 80 - 7
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 (
@@ -508,6 +508,8 @@ export default {
                 ["1", "2", "3"].includes(this.data.jsonData.echartsType)
               ) {
                 this.editEcharts();
+              }else{
+                this.changeShowIndex(1);
               }
             });
           })
@@ -586,7 +588,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--;
@@ -599,10 +601,11 @@ export default {
       _copyData.json_data = JSON.stringify(_copyData.jsonData);
       _oldCopyDate.json_data = JSON.stringify(_oldCopyDate.jsonData);
       // 一样就不用更新了
-      if (JSON.stringify(_copyData) == JSON.stringify(_oldCopyDate)) return;
+      // 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)
@@ -641,6 +644,7 @@ export default {
       });
     },
     editEcharts() {
+      console.log("生成图表")
       this.loading = true;
       this.loadNum = 1;
       this.openItem = false;
@@ -651,6 +655,18 @@ export default {
             if (res.length <= 0) {
               this.loadNum = 2;
               this.loading = false;
+              let _copyData = JSON.parse(JSON.stringify(this.data));
+            _copyData.json_data = JSON.stringify(_copyData.jsonData);
+            if (this.historyResult.length == 0) {
+              this.historyResult.push(_copyData.jsonData);
+            } else {
+              this.historyResult.splice(
+                this.showIndex + 1,
+                0,
+                _copyData.jsonData
+              );
+            }
+            this.changeShowIndex(1);
               return this.$message.error("在正文中未找到表格数据");
             }
 
@@ -708,6 +724,18 @@ export default {
             console.log(e);
             this.loadNum = 2;
             this.loading = false;
+            let _copyData = JSON.parse(JSON.stringify(this.data));
+            _copyData.json_data = JSON.stringify(_copyData.jsonData);
+            if (this.historyResult.length == 0) {
+              this.historyResult.push(_copyData.jsonData);
+            } else {
+              this.historyResult.splice(
+                this.showIndex + 1,
+                0,
+                _copyData.jsonData
+              );
+            }
+            this.changeShowIndex(1);
             this.$message.error("生成图表失败");
           }
         });
@@ -719,6 +747,18 @@ export default {
             if (res.length <= 0) {
               this.loadNum = 2;
               this.loading = false;
+              let _copyData = JSON.parse(JSON.stringify(this.data));
+            _copyData.json_data = JSON.stringify(_copyData.jsonData);
+            if (this.historyResult.length == 0) {
+              this.historyResult.push(_copyData.jsonData);
+            } else {
+              this.historyResult.splice(
+                this.showIndex + 1,
+                0,
+                _copyData.jsonData
+              );
+            }
+            this.changeShowIndex(1);
               return this.$message.error("在正文中未找到表格数据");
             }
 
@@ -779,6 +819,18 @@ export default {
             console.log(e);
             this.loadNum = 2;
             this.loading = false;
+            let _copyData = JSON.parse(JSON.stringify(this.data));
+            _copyData.json_data = JSON.stringify(_copyData.jsonData);
+            if (this.historyResult.length == 0) {
+              this.historyResult.push(_copyData.jsonData);
+            } else {
+              this.historyResult.splice(
+                this.showIndex + 1,
+                0,
+                _copyData.jsonData
+              );
+            }
+            this.changeShowIndex(1);
             this.$message.error("生成图表失败");
           }
         });
@@ -789,6 +841,18 @@ export default {
             if (res.length <= 0) {
               this.loadNum = 2;
               this.loading = false;
+              let _copyData = JSON.parse(JSON.stringify(this.data));
+            _copyData.json_data = JSON.stringify(_copyData.jsonData);
+            if (this.historyResult.length == 0) {
+              this.historyResult.push(_copyData.jsonData);
+            } else {
+              this.historyResult.splice(
+                this.showIndex + 1,
+                0,
+                _copyData.jsonData
+              );
+            }
+            this.changeShowIndex(1);
               return this.$message.error("在正文中未找到表格数据");
             }
 
@@ -888,6 +952,18 @@ export default {
             console.log(e);
             this.loadNum = 2;
             this.loading = false;
+            let _copyData = JSON.parse(JSON.stringify(this.data));
+            _copyData.json_data = JSON.stringify(_copyData.jsonData);
+            if (this.historyResult.length == 0) {
+              this.historyResult.push(_copyData.jsonData);
+            } else {
+              this.historyResult.splice(
+                this.showIndex + 1,
+                0,
+                _copyData.jsonData
+              );
+            }
+            this.changeShowIndex(1);
             this.$message.error("生成图表失败");
           }
         });
@@ -896,9 +972,6 @@ export default {
         this.loadNum = 2;
         return this.$message.error("该模板不是图表模板");
       }
-      this.loadNum = 2;
-      this.loading = false;
-      return this.$message.error("生成图表失败");
     },
     calculateTopValues(len, minTop = -80, maxTop = 70, maxStep = 40) {
       const length = len;

+ 203 - 160
src/components/pages/classroomObservation/components/analysisSpecialItem.vue

@@ -1,10 +1,14 @@
 <template>
-  <div class="analysisItem" ref="analysisItemRef" :style="
+  <div
+    class="analysisItem"
+    ref="analysisItemRef"
+    :style="
       `top:${moveTop}px;transition:${isDragging ? '0' : '.3s'}s;${
         isDragging ? 'z-index:999' : ''
       }`
     "
-		@mousedown="moveDown($event)">
+    @mousedown="moveDown($event)"
+  >
     <div class="ai-header">
       <div class="ai-h-left" @click.stop="changeOpenItem(!openItem)">
         <span
@@ -184,7 +188,10 @@
       <!-- 光谱图 -->
       <echartsSpectrogram
         style="max-height: 200px;max-width: 90%;width: 90%;height: 200px;margin: auto;margin-bottom: 50px;"
-        v-if="data.jsonData.spectrogramData && data.jsonData.mId == 'bfe844b1-7a45-11ef-9b30-005056b86db5'"
+        v-if="
+          data.jsonData.spectrogramData &&
+            data.jsonData.mId == 'bfe844b1-7a45-11ef-9b30-005056b86db5'
+        "
         :data="data.jsonData.spectrogramData"
       />
 
@@ -197,7 +204,11 @@
         "
       />
 
-			<echartsRTCH v-if="data.jsonData.RT && data.jsonData.CH" :data="{RT:data.jsonData.RT,CH:data.jsonData.CH}" style="width: 100%;height: 350px;display: flex;justify-content: center;margin-bottom: 20px;"/>
+      <echartsRTCH
+        v-if="data.jsonData.RT && data.jsonData.CH"
+        :data="{ RT: data.jsonData.RT, CH: data.jsonData.CH }"
+        style="width: 100%;height: 350px;display: flex;justify-content: center;margin-bottom: 20px;"
+      />
       <!-- <div class="rtCh" v-if="data.jsonData.RT && data.jsonData.CH">
         <img
           :src="
@@ -227,14 +238,14 @@ import { v4 as uuidv4 } from "uuid";
 import echartsSpectrogram from "./echartsSpectrogram";
 // import editNameDialog from './editNameDialog.vue'
 import markdownIt from "markdown-it";
-import echartsRTCH from './echartsRTCH'
+import echartsRTCH from "./echartsRTCH";
 export default {
   emits: ["delItem", "editItem", "saveItem"],
   components: {
     mdView,
     eChartTemplate,
     echartsSpectrogram,
-		echartsRTCH
+    echartsRTCH
     // editNameDialog
   },
   props: {
@@ -272,7 +283,7 @@ export default {
       type: Boolean,
       default: true
     },
-		isDrag: {
+    isDrag: {
       type: Boolean,
       default: false
     },
@@ -319,7 +330,7 @@ export default {
           }
         ]
       },
-			startY: 0,
+      startY: 0,
       startTop: 0,
       moveTop: 0,
       isDragging: false,
@@ -349,7 +360,7 @@ export default {
   },
   methods: {
     changeOpenItem(newValue) {
-			if(this.isDrag)return;
+      if (this.isDrag) return;
       if (this.loading == true && this.loadNum != 0)
         return this.$message("请稍后...");
       this.loadNum = 0;
@@ -459,7 +470,7 @@ export default {
     },
     getData(type = 0) {
       if (
-        this.data.jsonData.mId == 'bfe844b1-7a45-11ef-9b30-005056b86db5' &&
+        this.data.jsonData.mId == "bfe844b1-7a45-11ef-9b30-005056b86db5" &&
         type == 1
       ) {
         return this.getSpectrogram("", type);
@@ -503,7 +514,7 @@ export default {
         ) {
           this.getTeachingModeData(_result, type);
         } else if (
-          this.data.jsonData.mId == 'bfe844b1-7a45-11ef-9b30-005056b86db5'
+          this.data.jsonData.mId == "bfe844b1-7a45-11ef-9b30-005056b86db5"
         ) {
           return this.getSpectrogram(_result, type);
         } else {
@@ -513,6 +524,14 @@ export default {
         console.log(error);
         this.loadNum = 2;
         this.loading = false;
+        let _copyData = JSON.parse(JSON.stringify(this.data));
+        _copyData.json_data = JSON.stringify(_copyData.jsonData);
+        if (this.historyResult.length == 0) {
+          this.historyResult.push(_copyData.jsonData);
+        } else {
+          this.historyResult.splice(this.showIndex + 1, 0, _copyData.jsonData);
+        }
+        this.changeShowIndex(1);
         return this.$message.error("生成分析失败");
       }
     },
@@ -558,14 +577,14 @@ export default {
           trigger: "item",
           formatter: "{a} <br/>{b}: {d}%",
           textStyle: {
-            color: '#000000'
+            color: "#000000"
           }
         },
         legend: {
           top: "5%",
           left: "center",
           textStyle: {
-            color: '#000000'
+            color: "#000000"
           }
         },
         series: [
@@ -575,13 +594,13 @@ export default {
             radius: ["40%", "70%"],
             label: {
               formatter: "{b}: {d}%",
-              color: '#000000'
+              color: "#000000"
             },
             emphasis: {
               label: {
                 show: true,
                 formatter: "{b}: {d}%",
-                color: '#000000'
+                color: "#000000"
               },
               itemStyle: {
                 shadowBlur: 10,
@@ -594,7 +613,6 @@ export default {
         ]
       };
 
-
       let _copyData = JSON.parse(JSON.stringify(this.data));
       _copyData.jsonData.eChartData = _option;
       _copyData.json_data = JSON.stringify(_copyData.jsonData);
@@ -672,8 +690,7 @@ ${JSON.stringify(_dataList)}
             step: "start",
             data: _result,
             type: "line",
-            lineStyle: {
-            }
+            lineStyle: {}
           },
           {
             name: "对角线",
@@ -683,7 +700,7 @@ ${JSON.stringify(_dataList)}
               [_maxValue, _maxValue]
             ],
             lineStyle: {
-              type: "dashed",
+              type: "dashed"
             },
             markLine: {
               symbol: ["none", "none"]
@@ -800,18 +817,18 @@ CH:${_CH}
       // };
 
       let parm = {
-          id:"f8e1ebb2-2e0d-11ef-8bf4-12e77c4cb76b",
-          message: _msg,
-          session_name: uuidv4(),
-          userId: this.userId,
-          file_ids: this.fileId ? [this.fileId] : [],
-          model: "gpt-4o-2024-11-20",
-          sound_url:"",
-          temperature:0.2,
-          top_p:1,
-          max_completion_tokens:4096,
-          stream:false,
-          uid:uuidv4()
+        id: "f8e1ebb2-2e0d-11ef-8bf4-12e77c4cb76b",
+        message: _msg,
+        session_name: uuidv4(),
+        userId: this.userId,
+        file_ids: this.fileId ? [this.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
@@ -832,12 +849,11 @@ CH:${_CH}
               _copyData.jsonData
             );
           }
-          this.changeShowIndex(1);
+          this.changeShowIndex(1, false);
           this.loading = false;
-					this.$nextTick(()=>{
-
-						this.editEcharts(true);
-						})
+          this.$nextTick(() => {
+            this.editEcharts(true);
+          });
         })
         .catch(err => {
           this.loadNum = 2;
@@ -858,7 +874,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--;
@@ -871,10 +887,11 @@ CH:${_CH}
       _copyData.json_data = JSON.stringify(_copyData.jsonData);
       _oldCopyDate.json_data = JSON.stringify(_oldCopyDate.jsonData);
       // 一样就不用更新了
-      if (JSON.stringify(_copyData) == JSON.stringify(_oldCopyDate)) return;
+      // 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(":");
@@ -916,108 +933,124 @@ CH:${_CH}
     },
     getSpectrogram(_dataList, type = 1) {
       if (type === 0) {
-				try {
-					this.loading = true;
-				this.openItem = false;
-        return this.getContentTable().then(res => {
-          if (res.length <= 0) {
-            this.loadNum = 2;
-            this.loading = false;
-            return this.$message.error("在正文中未找到表格数据");
-          }
-
-          let _tableData = res;
-
-					let _delIndex = _tableData.findIndex(i=>i.includes(this.converter("时间点")))
-          console.log("👉===",_dataList,_tableData)
-					_tableData = _tableData.slice(_delIndex+1)
-          console.log()
-          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))
+        try {
+          this.loading = true;
+          this.openItem = false;
+          return this.getContentTable().then(res => {
+            if (res.length <= 0) {
+              this.loadNum = 2;
+              this.loading = false;
+              return this.$message.error("在正文中未找到表格数据");
             }
-          });
 
-           console.log(_result.reduce((pre,cur)=>{return pre+=cur.sumTime},0))
-          console.log("→xxx",_result)
-          let breakpoint = [];
+            let _tableData = res;
 
-					breakpoint = _tableData.map(i=>this.convertToSeconds(i[0]))
+            let _delIndex = _tableData.findIndex(i =>
+              i.includes(this.converter("时间点"))
+            );
+            console.log("👉===", _dataList, _tableData);
+            _tableData = _tableData.slice(_delIndex + 1);
+            console.log();
+            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));
+              }
+            });
 
-          _result = _result.filter(
-            i =>
-              i.identity == this.converter("老师") ||
-              i.identity == this.converter("学生")
-          );
-          // let
-          let _data = {
-            data: [],
-            breakpoint: []
-          };
+            console.log(
+              _result.reduce((pre, cur) => {
+                return (pre += cur.sumTime);
+              }, 0)
+            );
+            console.log("→xxx", _result);
+            let breakpoint = [];
 
-          _data.data = _result.map(i => ({role:i.identity,type:i.identity==this.converter("老师")?0:1,value:i.sumTime}));
-          _data.breakpoint = breakpoint;
-          let _copyData = JSON.parse(JSON.stringify(this.data));
-          _copyData.jsonData.spectrogramData = _data;
-          _copyData.json_data = JSON.stringify(_copyData.jsonData);
-          if (this.historyResult.length == 0) {
-            this.historyResult.push(_copyData.jsonData);
-          } else {
-            this.historyResult.splice(
-              this.showIndex + 1,
-              0,
-              _copyData.jsonData
+            breakpoint = _tableData.map(i => this.convertToSeconds(i[0]));
+
+            _result = _result.filter(
+              i =>
+                i.identity == this.converter("老师") ||
+                i.identity == this.converter("学生")
             );
-          }
-          this.changeShowIndex(1);
-          this.loading = false;
+            // let
+            let _data = {
+              data: [],
+              breakpoint: []
+            };
+
+            _data.data = _result.map(i => ({
+              role: i.identity,
+              type: i.identity == this.converter("老师") ? 0 : 1,
+              value: i.sumTime
+            }));
+            _data.breakpoint = breakpoint;
+            let _copyData = JSON.parse(JSON.stringify(this.data));
+            _copyData.jsonData.spectrogramData = _data;
+            _copyData.json_data = JSON.stringify(_copyData.jsonData);
+            if (this.historyResult.length == 0) {
+              this.historyResult.push(_copyData.jsonData);
+            } else {
+              this.historyResult.splice(
+                this.showIndex + 1,
+                0,
+                _copyData.jsonData
+              );
+            }
+            this.changeShowIndex(1);
+            this.loading = false;
 
-          // console.log(_dataList);
-          // console.log(_tableData);
-        });
-				} catch (e) {
-					this.loadNum = 2;
+            // console.log(_dataList);
+            // console.log(_tableData);
+          });
+        } catch (e) {
+          this.loadNum = 2;
           this.loading = false;
           return this.$message.error("数据格式错误");
-				}
+        }
       }
       this.$nextTick(() => {
         this.loading = true;
@@ -1060,13 +1093,13 @@ CH:${_CH}
           userId: this.userId,
           file_ids: this.fileId ? [this.fileId] : [],
           model: "gpt-4o-2024-11-20",
-          sound_url:"",
-          temperature:0.2,
-          top_p:1,
-          max_completion_tokens:4096,
-          stream:false,
-          uid:uuidv4()
-					// model: "gpt-4o-mini"
+          sound_url: "",
+          temperature: 0.2,
+          top_p: 1,
+          max_completion_tokens: 4096,
+          stream: false,
+          uid: uuidv4()
+          // model: "gpt-4o-mini"
         };
 
         // 👇
@@ -1130,10 +1163,9 @@ CH:${_CH}
             }
             this.changeShowIndex(1);
             this.loading = false;
-						this.$nextTick(()=>{
-
-							this.editEcharts(true);
-						})
+            this.$nextTick(() => {
+              this.editEcharts(true);
+            });
           })
           .catch(err => {
             this.loadNum = 2;
@@ -1177,8 +1209,8 @@ CH:${_CH}
         resolve(_contentTableList);
       });
     },
-		moveDown(e) {
-			if(!this.isDrag)return;
+    moveDown(e) {
+      if (!this.isDrag) return;
       this.isDragging = true;
       this.startY = e.clientY;
       this.$nextTick(() => {
@@ -1187,15 +1219,23 @@ CH:${_CH}
             ...this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
               .dragBoxRefTop
           );
-					if(this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs.dragBoxRefBottom.length>0){
-						this.dragBoxList.push(
-            ...this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
+          if (
+            this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
+              .dragBoxRefBottom.length > 0
+          ) {
+            this.dragBoxList.push(
+              ...this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
+                .dragBoxRefBottom
+            );
+          } else if (
+            this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
               .dragBoxRefBottom
-          );
-					}else if(this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs.dragBoxRefBottom){
-						this.dragBoxList.push(this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs.dragBoxRefBottom);
-					}
-
+          ) {
+            this.dragBoxList.push(
+              this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
+                .dragBoxRefBottom
+            );
+          }
         });
         // 禁用页面文本选择
         document.body.style.userSelect = "none";
@@ -1259,18 +1299,21 @@ CH:${_CH}
         if (!this.enterDrag) {
           this.moveTop = 0;
         } else {
-					this.moveTop = 0;
-					let moveData = this.enterDrag.getAttribute('type');
-					this.$emit("moveAnalysis",{form:`${this.data.Type}_${this.index}_${this.data.tIndex}`,to:moveData})
+          this.moveTop = 0;
+          let moveData = this.enterDrag.getAttribute("type");
+          this.$emit("moveAnalysis", {
+            form: `${this.data.Type}_${this.index}_${this.data.tIndex}`,
+            to: moveData
+          });
         }
         // 移除全局的鼠标移动和释放事件
         document.removeEventListener("mousemove", this.onMouseMove);
         document.removeEventListener("mouseup", this.stopDragging);
       } catch (error) {
-				this.moveTop = 0;
-				document.removeEventListener("mousemove", this.onMouseMove);
+        this.moveTop = 0;
+        document.removeEventListener("mousemove", this.onMouseMove);
         document.removeEventListener("mouseup", this.stopDragging);
-			}
+      }
     }
   },
   mounted() {
@@ -1294,7 +1337,7 @@ CH:${_CH}
   border: 1px solid #e7e7e7;
   border-radius: 4px;
   transition: 0.3s;
-	position: relative;
+  position: relative;
 }
 
 .analysisItem:hover {

+ 5 - 3
src/components/pages/classroomObservation/components/resourceLibraryDialog.vue

@@ -4,7 +4,7 @@
 			<div style="width: 100%;height: 100%;">
 					<fileBox type="2" ref="fileBoxRef" @addFile="addFile"></fileBox>
 			</div>
-	</el-dialog>    
+	</el-dialog>
 </template>
 
 <script>
@@ -38,7 +38,9 @@ export default {
 	methods: {
 		open(){
 			this.show = true;
-			this.$refs.fileBoxRef.getData();
+			this.$nextTick(()=>{
+        this.$refs.fileBoxRef.getData();
+      })
 		},
 		handleClose(done) {
 			this.close();
@@ -55,7 +57,7 @@ export default {
 					this.close();
 			}
 
-	}   
+	}
 };
 </script>
 

+ 684 - 53
src/components/pages/classroomObservation/dialog/batchCreationClassDialog.vue

@@ -35,7 +35,7 @@
             >
           </div>
           <div class="b_n_right">
-            <span>总计{{ dataList.length }}个</span>
+            <span @click="test">总计{{ dataList.length }}个</span>
           </div>
         </div>
         <div class="b_bottom">
@@ -48,7 +48,7 @@
                       selectStatus == "99" ||
                       statusList
                         .find(i => i.value == selectStatus)
-                        .allow.includes(i.state)
+                        .allow.includes(i.status)
                   ).length
                 }}</span
               >
@@ -82,6 +82,8 @@
               @changeChecked="changeCardChecked"
               @editBaseMessage="editBaseMessage"
               @changeData="changeData"
+              @taskBtn="taskBtn"
+              @goToEdit="goToEdit"
             />
           </div>
         </div>
@@ -95,6 +97,13 @@
         @success="editBaseMessageSuccess"
       />
     </el-dialog>
+    <!-- 录音转文字 -->
+    <iframe
+      allow="camera *; microphone *;display-capture;midi;encrypted-media;"
+      src="https://beta.cloud.cocorobo.cn/browser/public/index.html"
+      ref="iframeRef"
+      v-show="false"
+    ></iframe>
   </div>
 </template>
 
@@ -103,7 +112,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 +127,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: {
@@ -184,15 +197,10 @@ export default {
           (this.selectStatus == "99" ||
             this.statusList
               .find(i2 => i2.value == this.selectStatus)
-              .allow.includes(i.state))
+              .allow.includes(i.status))
       ).length;
     }
   },
-  watch: {
-    selectList(newValue, oldValue) {
-      console.log(newValue, oldValue);
-    }
-  },
   methods: {
     open() {
       this.show = true;
@@ -227,6 +235,8 @@ export default {
           { value: 1, name: "学科课堂分析", loading: false },
           { value: 2, name: "扩展分析", loading: false }
         ];
+      } else {
+        tagList = tagList.dialogTagList;
       }
 
       let batch = uuidv4();
@@ -241,9 +251,9 @@ export default {
         let data = {
           id: uuidv4(),
           create_at: "2025-05-07 16.05.03",
-          remarks: "备注",
+          remarks: "",
           batch: batch,
-          status: "3",
+          status: "0",
           createId: "",
           jsonData: {
             file_ids: "",
@@ -260,6 +270,7 @@ export default {
                 fileList1: [],
                 fileList2: [],
                 fileList3: [],
+                name: "课堂记录",
                 NephogramList: [],
                 videoList: []
               }
@@ -295,9 +306,15 @@ export default {
               text: "生成报告",
               status: "0",
               progress: "0"
+            },
+            {
+              type: "createClass",
+              text: "创建课堂",
+              status: "0",
+              progress: "0"
             }
           ];
-        } else if (i.type == "audio/wav") {
+        } else if (["audio/wav",'audio/x-m4a','audio/mpeg'].includes(i.type) ) {
           data.jsonData.baseMessage.imageList.fileList = [
             {
               name: i.name,
@@ -317,25 +334,39 @@ 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"
+              }
+            ]
+          );
         } else if (i.type == "video/mp4") {
           data.jsonData.baseMessage.imageList.videoList = [
             {
@@ -362,33 +393,48 @@ 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,
-                status: data.status,
+                status: "3",
                 json: JSON.stringify(data.jsonData),
                 batch: data.batch,
                 createId: data.createId,
@@ -401,6 +447,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);
                 }
@@ -417,6 +464,9 @@ export default {
       Promise.all(promiseList)
         .then(res => {
           this.$message.success("创建成功");
+          if(!this.dataList.find(i =>i.status == 1)){
+            this.startTask(this.dataList.find(i =>i.status === "0").id);
+          }
         })
         .catch(err => {
           console.log("创建出错", err);
@@ -460,7 +510,22 @@ export default {
     //修改课堂的baseMessage
     editBaseMessageSuccess({ editId, message }) {
       if (editId) {
+        let _editorBarData = null
+        let _transcriptionData = null;
+        if(this.dataList.find(i=>i.id===editId).jsonData.baseMessage.editorBarData){
+          _editorBarData = this.dataList.find(i=>i.id===editId).jsonData.baseMessage.editorBarData;
+        }
+
+        if(this.dataList.find(i=>i.id===editId).jsonData.baseMessage.transcriptionData){
+          _transcriptionData = this.dataList.find(i=>i.id===editId).jsonData.baseMessage.transcriptionData;
+        }
         this.dataList.find(i => i.id === editId).jsonData.baseMessage = message;
+        if(_editorBarData){
+          this.dataList.find(i=>i.id===editId).jsonData.baseMessage.editorBarData = _editorBarData;
+        }
+        if(_transcriptionData){
+          this.dataList.find(i=>i.id===editId).jsonData.baseMessage.transcriptionData = _transcriptionData;
+        }
         this.$forceUpdate();
         this.$refs.editBaseMessageDialogRef.close();
         this.updateTask(editId);
@@ -527,19 +592,59 @@ 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 dataCopy.jsonData.fileData.fileObj;
+          }
+          if (
+            dataCopy.jsonData.baseMessage.editorBarData &&
+            dataCopy.jsonData.baseMessage.editorBarData.content
+          ) {
+            delete dataCopy.jsonData.baseMessage.editorBarData.content;
+          }
+          if (
+            dataCopy.jsonData.baseMessage.editorBarData &&
+            dataCopy.jsonData.baseMessage.editorBarData.tableList
+          ) {
+            delete dataCopy.jsonData.baseMessage.editorBarData.tableList;
+          }
+
+          if(dataCopy.jsonData.errorMessage){
+            delete dataCopy.jsonData.errorMessage;
+          }
+
+          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 +675,532 @@ 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(!this.nowDoTaskId.includes(_startData.id))break;
+          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;
+            let audioFile = await this.getVideoToVoiceAndUploadMixin(_fileData);
+            if(audioFile.data==3){
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.errorMessage = {content:"视频提取音频失败",err:audioFile.err};
+
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).status = "3"
+              break;
+            }
+            if(!this.nowDoTaskId.includes(_startData.id))break;
+            _startData.jsonData.fileData.videoUrl = _fileData.url;
+            _startData.jsonData.fileData.fileObj = audioFile.fileObj;
+            _startData.jsonData.fileData.url = audioFile.audioUrl.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
+              )
+            );
+            _startData.jsonData.fileData.fileObj = audioFile.fileObj;//防止文件因为json失效
+            console.log("data👉",JSON.parse(JSON.stringify(_startData)))
+            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);
+              if(!this.nowDoTaskId.includes(_startData.id))break;
+              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)`);
+              console.log("data👉",JSON.parse(JSON.stringify(_startData)))
+            } else if (
+              ['video/mp4','audio/wav','audio/x-m4a','audio/mpeg'].includes(fileData.type)
+            ) {
+              //mp4与wav文件
+              _stepList[i].status = "2";
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.steps = _stepList;
+
+              let _file = _startData.jsonData.fileData.fileObj;
+              if(!_file && JSON.stringify(_file)!="{}"){
+                let fileBody = await this.getFileBody(_startData.jsonData.fileData.url);
+                let _type = _startData.jsonData.fileData.type.split('/')[1];
+                if(_type=='x-m4a'){
+                  _type="m4a"
+                }else if(_type=="mpeg"){
+                  _type = "mp3"
+                }
+                _file = new File([fileBody.data], `audio.${_type}`, { type: _startData.jsonData.fileData.type });
+              }
+
+              if (["audio/x-m4a","audio/mpeg"].includes(_file.type)) {
+                let _data = await this.audioToWavMixin(_file);
+                if(_data!==1){
+                  _file = _data.data;
+                }else{
+                  console.log("报错👉",_data)
+                }
+              }
+              console.log("转录文件",_file)
+
+              let {
+                transcriptionContent,
+                editorBarData
+              } = await this.wavAudioToTextAndObjMixin(_file);
+
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.baseMessage.transcriptionData = transcriptionContent;
+              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}:已获取转录文稿(mp4/wav)`);
+            }
+          } else if (_step.type === "automaticCoding") {
+            if (_startData.jsonData.automaticCoding) {
+              _stepList[i].status = "2";
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.steps = _stepList;
+              //选择了字段编码
+              let _content =
+                _startData.jsonData.baseMessage.editorBarData.content;
+              let _tableList =
+                _startData.jsonData.baseMessage.editorBarData.table;
+              if (!_content) {
+                _content = await this.getFile(
+                  _startData.jsonData.baseMessage.editorBarData.url
+                );
+                _content = _content.data;
+                this.dataList.find(
+                  i => i.id === _startData.id
+                ).jsonData.baseMessage.editorBarData.content = _content;
+              }
+              if(!_tableList){
+                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,
+                    value: i.cells[3].textContent,
+                    time: i.cells[4].textContent,
+                    role: i.cells[5] ? i.cells[5].textContent : "",
+                    code: i.cells[6] ? i.cells[6].textContent : ""
+                  };
+                  _result.push(obj);
+                });
+
+                _tableList = _result;
+              }
+
+              let { editorBarData} = await this.automaticCodingMixin({ tableList: _tableList});
+              if(!this.nowDoTaskId.includes(_startData.id))break;
+
+              _stepList[i].status = "1";
+
+              this.dataList.find(i => i.id === _startData.id).jsonData = _startData.jsonData;
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.baseMessage.editorBarData = editorBarData;
+
+              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("data👉",JSON.parse(JSON.stringify(_startData)))
+              console.log(`${_startData.id}:已获完成自动编码`);
+            } 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.baseMessage.editorBarData;
+            let { fileId } = await this.getFileIdMixin(_fileData.url);
+            if(!this.nowDoTaskId.includes(_startData.id))break;
+            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("data👉",JSON.parse(JSON.stringify(_startData)))
+            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(!this.nowDoTaskId.includes(_startData.id))return resolve();
+                  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}:已完成分析获取`);
+              if(!this.nowDoTaskId.includes(_startData.id))return;
+              console.log("data👉",JSON.parse(JSON.stringify(_startData)))
+              console.log(_startData.jsonData.analysisList);
+              _stepList[i].status = "1";
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.steps = _stepList;
+              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
+                )
+              );
+            });
+            if(!this.nowDoTaskId.includes(_startData.id))break;
+          } 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);
+
+            if([1,2,3].includes(newOption.data)){
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).jsonData.errorMessage = {content:"创建课堂失败",err:data.err};
+
+              this.dataList.find(
+                i => i.id === _startData.id
+              ).status = "3"
+              break;
+
+            }
+            if(!this.nowDoTaskId.includes(_startData.id))break;
+
+            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);
+            }
+          }
+        }
+      }
+    },
+    // 暂停任务
+    pauseTask(id) {
+      let _waitTask = this.dataList.find(i => i.status === "0");
+      if (_waitTask) {
+        //暂停这个任务,
+        this.dataList.find(i => i.id === id).status = "0";
+        this.nowDoTaskId = this.nowDoTaskId.filter(i=>i!==id);
+        // 开始另一个任务
+        this.startTask(_waitTask.id);
+      } else {
+        //没有任务了,所以改为待开始(停止)
+        this.dataList.find(i => i.id === id).status = "3";
+      }
+    },
+    //停止任务
+    stopTask(id) {
+      let stopTask = this.dataList.find(i=>i.id===id);
+      if(stopTask.status==='0'){
+        this.dataList.find(i => i.id === id).status = "3";
+      }if(stopTask.status==='1'){
+        this.dataList.find(i => i.id === id).status = "3";
+        this.nowDoTaskId = this.nowDoTaskId.filter(i=>i!==id);
+      }
+
+      console.log(stopTask)
+
+      let _waitTask = this.dataList.find(i => i.status === "0");
+      console.log(_waitTask)
+      if(_waitTask){
+        // 开始另一个任务
+        this.startTask(_waitTask.id);
+      }
+      // let _waitTask = this.dataList.find(i => i.status === "0" && i.id===id);
+      // if (_waitTask) {
+      //   //停止这个任务,
+      //   this.dataList.find(i => i.id === id).status = "3";
+      //   this.nowDoTaskId = this.nowDoTaskId.filter(i=>i!==id);
+      //   // 开始另一个任务
+      //   this.startTask(_waitTask.id);
+      // } else {
+      //   //只停止这个任务
+      //   this.dataList.find(i => i.id === id).status = "3";
+      // }
+    },
+    goToEdit(id){
+      let _data = this.dataList.find(i=>i.id===id);
+      if(_data){
+        let createId = _data.jsonData.createId;
+        this.$emit("changeClass",createId);
+        this.close();
+      }
+    },
+    test() {
+      this.testMixin();
     }
   },
   mounted() {
     this.getTaskList();
+    this.getDialogTagDataList();
   }
 };
 </script>
@@ -639,7 +1266,7 @@ export default {
 .b_head {
   width: 100%;
   height: 50px;
-  margin: 20px 0 10px 0;
+  margin: 20px 0 0px 0;
   box-sizing: border-box;
 }
 
@@ -660,10 +1287,12 @@ export default {
 }
 
 .b_n_left {
-  width: calc(100% - 60px);
+  flex: 1;
+  /* width: calc(100% - 60px); */
   height: 100%;
   display: flex;
   align-items: center;
+  overflow-y: hidden;
 }
 
 .b_n_left > span {
@@ -676,6 +1305,7 @@ export default {
   cursor: pointer;
   transition: 0.3s;
   font-size: 16px;
+  white-space: nowrap;
 }
 
 .b_n_left > span:hover {
@@ -699,13 +1329,14 @@ export default {
 }
 
 .b_n_right {
-  width: 60px;
+  width: auto;
   height: 100%;
   display: flex;
   align-items: center;
   justify-content: flex-end;
   color: rgba(150, 155, 163, 1);
   font-size: 16px;
+  white-space: nowrap;
 }
 
 .b_bottom {

+ 141 - 48
src/components/pages/classroomObservation/dialog/editBaseMessageDialog.vue

@@ -211,7 +211,7 @@
   					>
 						<div class="m_m_box">
 							<el-button-group style="width: 100%;display: flex;justify-content: center;">
-							  <el-button size="small" @click="localUploadVideo()">本地上传</el-button>
+							  <el-button size="small" @click="addVideo()">本地上传</el-button>
 								<el-button size="small" @click="resourceUploadVideo()">资源库上传</el-button>
 							</el-button-group>
 						</div>
@@ -340,6 +340,7 @@ export default {
 		return {
 			loading: false,
 			show: false,
+      userId: this.$route.query["userid"],
       uploadVideoLoading:false,
       uploadImageLoading:false,
       uploadNephogramLoading:false,
@@ -458,11 +459,29 @@ export default {
     },
     //资源库上传
     resourceUploadVideo(){
-      this.$message.info("资源库上传")
+      this.$refs.resourceLibraryDialogRef.open();
     },
     //添加视频
     addVideo(){
-      this.$message.info("添加视频")
+      let input = document.createElement("input");
+			input.type = "file";
+			input.accept = "video/*";
+			input.click();
+			input.onchange = () => {
+				this.progressData.uploadVideo = true;
+				// this.uploadVideoLoading = true;
+				this.progressData.stop = false;
+				this.progressData.status = "";
+				this.progressData.value = 0;
+				let file = input.files[0];
+				this.uploadFileObj = file;
+				this.$nextTick(()=>{
+					this.$refs.uploadFileRef.awsupload({
+					file:file,
+					folderName:this.editId
+				})
+				})
+			}
     },
     //删除词云图
     delNephogram(){
@@ -470,8 +489,20 @@ export default {
     },
     //生成词云图
     addNephogram(){
-      this.$message.info("生成词云图")
+      // this.$message.info("生成词云图")
     },
+    delVideo(key){
+			this.$confirm("确定删除该视频吗?", "提示", {
+				confirmButtonText: "确定",
+				cancelButtonText: "取消",
+				type: "warning",
+			}).then(() => {
+				this.data.imageList.videoList = [];
+        this.$message.success("删除成功")
+			}).catch(e=>{
+				console.log("取消删除")
+			});
+		},
     //资源库添加文件
     resourceLibraryDialogAddFile(file){
       let _file = file[0];
@@ -481,17 +512,13 @@ export default {
 			}
       console.log(_file)
       this.$message.info("上传文件")
-			// this.$emit('saveVideo',{
-			// 		name: _file.name,
-			// 		status: "success",
-			// 		uid: _file.id,
-			// 		url: _file.file,
-			// })
-			// this.$message.success("上传成功")
-
-			// this.$confirm("是否提取视频音频并上传?","提示").then(()=>{
-			// 	this.getVideoVoice({url:_file.file});
-			// })
+      this.data.imageList.videoList = [{
+        name: _file.name,
+				status: "success",
+				uid: _file.id,
+				url: _file.file,
+      }]
+			this.$message.success("上传成功")
     },
     videoProgressUpdate(data){
 			if(data.status=="processing"){
@@ -538,42 +565,108 @@ export default {
 			let {data} = res;
 			this.$refs.uploadFileRef.file = null;
       console.log(data)
-			// this.$emit('saveVideo',{
-			// 		name: data.Key,
-			// 		status: "success",
-			// 		uid: "qgt",
-			// 		url: data.Location,
-			// })
-			// this.$message.success("上传成功");
-			// // this.uploadFileObj = null;
-			// this.$confirm("是否提取视频音频并上传?","提示").then(()=>{
-			// 	this.getVideoVoice({file:this.uploadFileObj});
-			// 	this.uploadFileObj = null;
-			// }).catch(err=>{
-			// 	this.uploadFileObj = null;
-			// })
+      this.data.imageList.videoList = [{
+        name: data.Key,
+					status: "success",
+					uid: this.userId,
+					url: data.Location,
+      }]
+      this.$message.success("上传成功");
 		},
     //上传课堂图片
     addImage2(){
-      this.$message.info("上传课堂图片")
-			// let input = document.createElement("input");
-			// input.type = "file";
-			// input.accept = "image/*";
-			// input.multiple = true;
-			// input.click();
-
-
-			// input.onchange = () => {
-			// 	this.uploadImageLoading = true;
-      //   let promise =[];
-      //   for(let i=0;i<input.files.length;i++){
-      //     promise.push(this.uploadFile(input.files[i]));
-      //   }
-      //   Promise.all(promise).then(res=>{
-      //     this.$emit("saveImage2",res);
-      //     this.uploadImageLoading = false;
-      //   })
-			// };
+			let input = document.createElement("input");
+			input.type = "file";
+			input.accept = "image/*";
+			input.multiple = true;
+			input.click();
+
+
+			input.onchange = () => {
+				this.uploadImageLoading = true;
+        let promise =[];
+        for(let i=0;i<input.files.length;i++){
+          promise.push(this.uploadFile(input.files[i]));
+        }
+        Promise.all(promise).then(res=>{
+          this.saveImage2(res)
+          // this.$emit("saveImage2",res);
+          this.uploadImageLoading = false;
+        })
+			};
+    },
+    saveImage2(dataList){
+      for(let i=0;i<dataList.length;i++){
+        if (this.data.imageList.fileList1.length == 0) {
+          this.data.imageList.fileList1.push(dataList[i]);
+        } else if (this.data.imageList.fileList2.length == 0) {
+          this.data.imageList.fileList2.push(dataList[i]);
+        } else if (this.data.imageList.fileList3.length == 0) {
+          this.data.imageList.fileList3.push(dataList[i]);
+        } else {
+          this.$message.error("最多只能上传3张图片");
+          break;
+        }
+      }
+    },
+    delImage(key){
+      this.$confirm("确定删除该图片吗?", "提示", {
+				confirmButtonText: "确定",
+				cancelButtonText: "取消",
+				type: "warning",
+			}).then(() => {
+        this.data.imageList[key] = [];
+				// this.$emit("delImage", key);
+			});
+    },
+    uploadFile(file){
+      return new Promise(resolve=>{
+        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) {
+							//这里可以写进度条
+							// console.log("Uploaded : " + parseInt((evt.loaded * 80) / evt.total) + '%');
+						})
+						.send(function (err, data) {
+							if (err) {
+								resolve(0)
+							} else {
+                resolve({
+									name: data.key,
+									status: "success",
+									uid: "1",
+									url: data.Location,
+								})
+							}
+						});
+				}
+      })
     },
     previewVideo(url){
       this.$refs.previewVideoDialogRef.open(url)

+ 122 - 15
src/components/pages/classroomObservation/dialog/uploadFileToCreateClassDialog.vue

@@ -24,17 +24,17 @@
               <div class="fl_item" v-for="item in fileList" :key="item.index">
                 <div class="fl_i_left">
                   <img
-                   v-if="item.type =='text/plain'"
+                   v-if="['text/plain'].includes(item.type)"
                     src="../../../../assets/icon/classroomObservation/textFile_icon.svg"
                   />
 
                   <img
-                   v-if="item.type =='audio/wav'"
+                   v-if="['audio/wav','audio/x-m4a','audio/mpeg'].includes(item.type)"
                     src="../../../../assets/icon/classroomObservation/audio_file.svg"
                   />
 
                   <img
-                   v-if="item.type =='video/mp4'"
+                   v-if="['video/mp4'].includes(item.type)"
                     src="../../../../assets/icon/classroomObservation/videoFile_icon.svg"
                   />
                 </div>
@@ -80,7 +80,7 @@
               <img
                 src="../../../../assets/icon/classroomObservation/file_processing.svg"
               />
-              <span>文件格式支持:mp4、wav、txt 文件</span>
+              <span>文件格式支持:mp4、mp3、wav、m4a、txt 文件</span>
             </div>
           </div>
           <div class="b_m_right">
@@ -207,12 +207,13 @@ export default {
       let _name = this.fileList.find(i => i.index == res.index).file.name;
       let size = this.fileList.find(i => i.index == res.index).file.size;
       let _type = this.fileList.find(i => i.index == res.index).type;
-      console.log(data);
       this.fileList.find(i => i.index == res.index).successData = {
         name: _name,
         url: data.Location,
         type: _type,
-        size: size
+        size: size,
+        duration: this.fileList.find(i => i.index == res.index).duration,
+        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");
@@ -227,32 +228,39 @@ export default {
         console.log("上传完成");
       }
     },
-    addFile() {
+    async addFile() {
       let input = document.createElement("input");
       input.type = "file";
-      input.accept = "video/mp4, audio/wav, text/plain";
+      input.accept = "video/mp4, audio/wav, audio/x-m4a, audio/mpeg, text/plain";
       input.multiple = true; // 支持多文件上传
       input.style.display = "none";
 
       input.click();
 
-      input.addEventListener("change", e => {
+      input.addEventListener("change", async e => {
         let files = e.target.files;
+        console.log(files)
         for (let i = 0; i < files.length; i++) {
+
           if (
-            ["video/mp4", "audio/wav", "text/plain"].includes(files[i].type)
+            ["video/mp4", "audio/wav", "audio/x-m4a", "text/plain","audio/mpeg"].includes(files[i].type)
           ) {
+            let _file = files[i];
+            if(_file.type==='audio/wav'){
+              _file = new File([_file], _file.name.toLowerCase(), {type: _file.type});
+            }
             this.fileList.push({
-              file: files[i],
+              file: _file,
               index: uuidv4(),
               successData: null,
-              name: files[i].name,
-              type: files[i].type,
+              name: _file.name,
+              fileObj:_file,
+              type: _file.type,
               progress: { status: "", percent: 0, key: "", uploadid: "" },
               status: "wait"
             });
           } else {
-            this.$message.info("文件格式不支持,仅支持mp4、wav、txt文件。");
+            this.$message.info("文件格式不支持,仅支持mp4、mp3、wav、m4a、txt文件。");
           }
         }
         if (!this.fileList.some(i => i.status === "uploading")) {
@@ -269,6 +277,26 @@ export default {
             });
           }
         }
+
+        if(this.fileList.length>0){
+          console.log("获取音频时长")
+          for(let i = 0;i<this.fileList.length;i++){
+            console.log(this.fileList[i])
+            if(["audio/wav", "audio/x-m4a","audio/mpeg"].includes(this.fileList[i].type)){
+              try {
+                let _time = await this.getAudioDuration(this.fileList[i].file);
+                this.fileList[i].duration = this.updateRecordedTimeMixin({duration:_time.toFixed(2)});
+                console.log(`音频时长:${this.updateRecordedTimeMixin({duration:_time.toFixed(2)})}`)
+              } catch (error) {
+                console.log(error)
+                continue
+              }
+
+            }else{
+              continue;
+            }
+          }
+        }
       });
     },
     async getTemplateData() {
@@ -306,7 +334,86 @@ export default {
       } catch (error) {
         console.log("获取模板失败");
       }
-    }
+    },
+    //获取音频文件的时长
+    async getAudioDuration(file) {
+      // 方法1:使用 <audio> 元素(更快更轻量)
+      const mediaElementMethod = () =>
+        new Promise((resolve, reject) => {
+          const audio = document.createElement("audio");
+          const url = URL.createObjectURL(file);
+
+          audio.preload = "metadata";
+          audio.src = url;
+
+          const cleanup = () => {
+            URL.revokeObjectURL(url);
+            audio.remove();
+          };
+
+          audio.onloadedmetadata = () => {
+            if (isFinite(audio.duration) && audio.duration > 0) {
+              cleanup();
+              resolve(audio.duration);
+            } else {
+              reject(new Error("无法通过元数据获取时长"));
+            }
+          };
+
+          audio.onerror = () => {
+            cleanup();
+            reject(new Error("音频加载错误"));
+          };
+
+          // iOS Safari 兼容处理
+          audio.load();
+          document.body.appendChild(audio);
+        });
+
+      // 方法2:使用 Web Audio API(更精确但更耗资源)
+      const webAudioMethod = () =>
+        new Promise((resolve, reject) => {
+          const reader = new FileReader();
+
+          reader.onload = async () => {
+            try {
+              const audioContext = new (window.AudioContext ||
+                window.webkitAudioContext)();
+              const buffer = await audioContext.decodeAudioData(reader.result);
+              resolve(buffer.duration);
+            } catch (e) {
+              reject(new Error("音频解码失败: " + e.message));
+            }
+          };
+
+          reader.onerror = () => reject(new Error("文件读取失败"));
+          reader.readAsArrayBuffer(file);
+        });
+
+      // 优先尝试快速方法,失败时回退到精确方法
+      try {
+        return await mediaElementMethod();
+      } catch (firstError) {
+        console.warn("快速方法失败:", firstError.message, "尝试精确方法...");
+        try {
+          return await webAudioMethod();
+        } catch (secondError) {
+          throw new Error(`所有方法均失败: ${firstError.message} > ${secondError.message}`);
+        }
+      }
+    },
+    //时间
+    updateRecordedTimeMixin({ duration }) {
+      // 更新currentTime,将秒数转换为时分秒格式
+      let hours = Math.floor(duration / 3600);
+      let minutes = Math.floor((duration % 3600) / 60);
+      let seconds = Math.floor(duration % 60);
+      return `${hours
+        .toString()
+        .padStart(2, "0")}:${minutes
+          .toString()
+          .padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
+    },
   }
 };
 </script>

+ 8 - 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()">
           <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" @changeClass="changeTid"/>
 
     <!-- <addNewCourseDialog
 			:courseList="optionData"
@@ -293,6 +293,7 @@ export default {
   methods: {
     //切换了课堂
     changeTid(newValue) {
+      if(this.tid!=newValue)this.tid = newValue;
       this.$nextTick(async () => {
         this.getFileIdId();
         this.$refs.messageAreaRef.getData();
@@ -388,6 +389,7 @@ export default {
           userid: this.userId,
           template: json
         };
+        console.log("创建新课堂",params);
         this.ajax
           .post(
             "https://gpt4.cocorobo.cn/insert_classroom_observation_template",
@@ -1214,6 +1216,9 @@ export default {
     batchBtn(){
       this.$refs.batchCreationClassDialogRef.open();
     },
+    addNewCourseOption(newOption){
+      this.optionData.unshift(newOption);
+    },
   },
   mounted() {
     this.getCourseList().then(_ => {

+ 199 - 29
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="['text/plain'].includes(cardData.jsonData.fileData.type)"
               src="../../../../assets/icon/classroomObservation/file_icon.svg"
             />
             <img
-              v-if="cardData.jsonData.fileData.type=='audio/wav'"
+              v-if="['audio/wav','audio/x-m4a','audio/mpeg'].includes(cardData.jsonData.fileData.type)"
               src="../../../../assets/icon/classroomObservation/audio_file.svg"
             />
             <img
-              v-if="cardData.jsonData.fileData.type=='video/mp4'"
+              v-if="['video/mp4'].includes(cardData.jsonData.fileData.type)"
               src="../../../../assets/icon/classroomObservation/videoFile_icon.svg"
             />
           </div>
@@ -24,20 +24,67 @@
             <div>
               <span>{{ cardData.jsonData.baseMessage.courseName }}</span>
               <img
+                v-show="cardData.status!='2'"
                 src="../../../../assets/icon/classroomObservation/table_edit.svg"
                 @click="editBaseMessage"
               />
             </div>
-            <span>{{ cardData.create_at }}</span>
+            <span>
+              <div>{{ cardData.create_at }}</div>
+              <span v-if="cardData.jsonData.fileData && cardData.jsonData.fileData.duration"></span>
+              <div v-if="cardData.jsonData.fileData && cardData.jsonData.fileData.duration">{{ cardData.jsonData.fileData.duration }}</div>
+            </span>
           </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 style="cursor: pointer;" @click="changeShowSteps(!showSteps)">处理中</span>
+            <div class="stepBox" v-show="showSteps">
+              <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 +92,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) && cardData.jsonData.createId"
             >前往编辑</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,19 +152,20 @@ export default {
         return {};
       }
     },
-    isSelect:{
-      type:Boolean,
-      default:false
-    },
+    isSelect: {
+      type: Boolean,
+      default: false
+    }
   },
   data() {
     return {
       checked: false,
+      showSteps:true,
       cardData: {
         id: "1",
         name: "文件名称文件名称文件名称",
         create_at: "2025-05-07 16.05.03",
-        remarks: "备注1",
+        remarks: "",
         status: "0",
         jsonData: {
           baseMessage: {
@@ -152,7 +229,7 @@ export default {
     };
   },
   watch: {
-    isSelect(newValue){
+    isSelect(newValue) {
       this.checked = newValue;
     },
     // checked(newValue) {
@@ -167,10 +244,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,32 +256,51 @@ export default {
   },
   methods: {
     editBaseMessage() {
-      this.$emit("editBaseMessage",this.cardData.id);
+      this.$emit("editBaseMessage", this.cardData.id);
     },
     //前往编辑
     goToEdit() {
-      this.$message.info("前往编辑");
+      this.$emit("goToEdit",this.cardData.id)
     },
     //查看报告
     lookReport() {
-      this.$message.info("查看报告");
+      window.topU.postMessage(
+        {
+          tools: "classroom_observation_board",
+          type: this.cardData.jsonData.createId
+        },
+        "*"
+      );
+      // this.$message.info("查看报告");
     },
     //重新生成
     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 });
+    },
+    changeShowSteps(newValue){
+      this.showSteps = newValue;
+    },
   },
   mounted() {
     if (this.data) {
@@ -281,6 +377,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 {
@@ -314,10 +462,19 @@ export default {
 .bcc_r_t_l_message > span {
   margin-top: 10px;
   font-size: 14px;
-  display: block;
+  display: flex;
+  align-items: center;
   color: rgba(150, 155, 163, 1);
 }
 
+.bcc_r_t_l_message > span>span{
+  width: 2px;
+  height: 12px;
+  display: block;
+  background-color: #CBCBCB;
+  margin: 0 10px;
+}
+
 .bcc_r_bottom {
   width: 100%;
   display: flex;
@@ -381,4 +538,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>

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

@@ -0,0 +1,1476 @@
+import {
+  v4 as uuidv4
+} from 'uuid';
+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: {
+    testMixin() {
+
+    },
+    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 this.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: 2,err:"未找到文件" })
+        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, fileObj: audioFile })
+              })
+            } catch (error) {
+              console.log("👉", error);
+              return resolve({ data: 2,err:error })
+            }
+
+          }
+          reader.readAsArrayBuffer(_file);
+        } catch (error) {
+          console.log("👉", error);
+          return resolve({ data: 2,err:error })
+        }
+      })
+    },
+    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({
+      });
+    },
+    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
+
+        });
+      });
+    },
+    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 })
+                        })
+                      }).catch(err => {
+                        console.log("修改基础信息失败", err)
+                        resolve({ data: 3, err: err })
+                      })
+                  }
+                }).catch(err => {
+                  resolve({ data: 2, err: err })
+                  console.log("存储fileId失败")
+                  console.log(err)
+                })
+            }
+          }).catch(err => {
+            resolve({ data: 1, err: err })
+            console.log("创建课堂失败")
+            console.log(err)
+          })
+      })
+    },
+    //文本转录
+    wavAudioToTextAndObjMixin(file) {
+      return new Promise(async (resolve) => {
+        let iframeRef = this.$refs["iframeRef"];
+        iframeRef.contentWindow.window.document.getElementById(
+          "languageOptions"
+        ).selectedIndex = 2;//默认普通话
+
+        let transcriptionContent = "";
+        let tableContent = "";
+        let tableList = [];
+        let _startTime = 0;
+        let _endTime = 0;
+
+        // 转录中
+        iframeRef.contentWindow.onRecognizedResult = (e) => {
+          let privText = e.privText;
+          let privSpeakerId = e.privSpeakerId;
+          let privDuration = e.privDuration;
+          let privOffset = e.privOffset;
+          if (!privText || !privSpeakerId || privSpeakerId == "Unknown") {//不记录
+            return;
+          }
+
+          _endTime = (privOffset + privDuration) / 10000000;
+
+          tableList.push({
+            value: privText,
+            startTime: this.updateRecordedTimeMixin({ duration: _startTime }),
+            endTime: this.updateRecordedTimeMixin({ duration: _endTime }),
+            time: this.updateRecordedTimeMixin({ duration: _endTime - _startTime }),
+            role: privSpeakerId,
+            code: ""
+          });
+
+          _startTime = _endTime;
+          transcriptionContent += privText;
+        };
+
+        //转录结束
+        iframeRef.contentWindow.onSessionStopped = async (e) => {
+          tableContent = `<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>`;
+          tableList.forEach((item, index) => {
+            tableContent += `<tr>
+							<td>${index + 1}</td>
+							<td>${item.startTime}</td>
+							<td>${item.endTime}</td>
+							<td>${item.value}</td>
+							<td>${item.time}</td>
+							<td>${item.role}</td>
+							<td>${item.code}</td>
+						</tr>`
+          })
+
+          tableContent += `<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr></tbody></table>`
+
+          var blob = new Blob([tableContent], { type: "text/plain;charset=utf-8" });
+          blob.lastModifiedDate = new Date();
+          blob.name = `classroomObservation.txt`;
+          this.uploadFileMixin(blob).then(upload => {
+            resolve({ transcriptionContent: transcriptionContent, editorBarData: { type: "0", url: upload.Location, content: tableContent, tableList: tableList } })
+          })
+        };
+
+        //开始转录
+        iframeRef.contentWindow.ConversationTranscriber({
+          files: [file]
+        });
+      })
+
+    },
+    updateRecordedTimeMixin({ duration }) {
+      // 更新currentTime,将秒数转换为时分秒格式
+      let hours = Math.floor(duration / 3600);
+      let minutes = Math.floor((duration % 3600) / 60);
+      let seconds = Math.floor(duration % 60);
+      // this.recordedForm.time = `${hours.toString().padStart(2, "0")}:${minutes
+      // 	.toString()
+      // 	.padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
+      return `${hours
+        .toString()
+        .padStart(2, "0")}:${minutes
+          .toString()
+          .padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
+    },
+    //自动编码
+    automaticCodingMixin(data) {
+      return new Promise(async (resolve) => {
+        let { tableList } = data;
+
+        let roleObj = {};
+        let tableContent = `<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>`;
+
+        console.log("说话人身份编码开始")
+        // 说话人身份编码
+        while (tableList.some(i => i.role.indexOf("Guest") != -1 && i.role !== '')) {
+          let _ajaxList = tableList.filter(i => i.role.indexOf("Guest") != -1 && i.role !== '').slice(0, 10);
+          console.log(`说话人身份编码:`, _ajaxList)
+          const params = {
+            inputs: {
+              options: "老师,学生",
+              rows: JSON.stringify(
+                _ajaxList.map(i => {
+                  return { content: i.value, role: i.role };
+                })
+              )
+            },
+            response_mode: "blocking",
+            user: this.userId
+          };
+
+          let roleRes = await this.getWavRoleList(params);
+          if (roleRes.data === 1) continue;;
+          let _roleResult = roleRes.data.data.outputs.result;
+          let _numRole = [];
+
+          _roleResult.forEach((txt, index) => {
+            let _oldRole = _ajaxList[index].role;
+            if (_numRole.map(i => i.role).includes(_oldRole)) {
+              let _findIndex = _numRole.findIndex(
+                i => i.role == _oldRole
+              );
+              if (txt == "学生") {
+                _numRole[_findIndex].s += 1;
+              } else if (txt == "老师") {
+                _numRole[_findIndex].t += 1;
+              }
+            } else {
+              if (txt == "学生") {
+                _numRole.push({ role: _oldRole, t: 0, s: 1 });
+              } else if (txt == "老师") {
+                _numRole.push({ role: _oldRole, t: 1, s: 0 });
+              }
+            }
+          });
+
+          //根据数量判断是老师还是学生
+          _numRole.forEach(i => {
+            if (i.t > i.s) {
+              roleObj[i.role] = "老师";
+            } else if (i.t < i.s) {
+              roleObj[i.role] = "学生";
+            }
+          });
+
+          //已经有的role
+          let roleKeys = Object.keys(roleObj);
+
+          tableList.forEach(i => {
+            if (roleKeys.includes(i.role)) {
+              i.role = roleObj[i.role];
+            }
+          });
+        }
+        console.log("说话人身份编码完成")
+
+
+        console.log("说话人行为编码开始")
+        //说话人行为编码
+        while (tableList.some(i => i.code == "" && i.role.indexOf("Guest") == -1 && i.value != "")) {
+          let _ajaxList = tableList.filter(i => i.code == "" && i.role.indexOf("Guest") == -1 && i.value != "").slice(0, 10);
+          console.log(`说话人行为编码:`, _ajaxList)
+          let params = {
+            inputs: {
+              rows: JSON.stringify(
+                _ajaxList.map(i => ({
+                  content: i.value,
+                  role: i.role
+                }))
+              ),
+              options: "老师讲课,老师提问或点名,老师板书或操作,老师评价或反馈,老师其他,学生发言,学生小组活动,学生自主学习,学生汇报分享,学生其他",
+              attention: "- 先根据说话人角色判断,再在对应角色的选项中选择选项\n- 如果没有合适的选项,默认使用`老师其他`或者`学生其他`"
+            },
+            response_mode: "blocking",
+            user: this.userId
+          };
+
+          let _codeRes = await this.getBehavioralCoding(params)
+          if (_codeRes.data === 1) continue;
+          const _codeResult = _codeRes.data.data.outputs.result;
+
+          _ajaxList.forEach((item, index) => {
+            let _findIndex = tableList.findIndex(i => i.index === item.index);
+            if (_findIndex != -1) {
+              tableList[_findIndex].code = _codeResult[index];
+            }
+          })
+        }
+        console.log("说话人行为编码完成")
+
+        tableList.forEach((item, index) => {
+          tableContent += `<tr>
+          <td>${index + 1}</td>
+          <td>${item.startTime}</td>
+          <td>${item.endTime}</td>
+          <td>${item.value}</td>
+          <td>${item.time}</td>
+          <td>${item.role}</td>
+          <td>${item.code}</td>
+        </tr>`
+        })
+        tableContent += `<tr><td></td><td></td><td></td><td></td><td></td><td></td><td></td></tr></tbody></table>`
+
+        var blob = new Blob([tableContent], { type: "text/plain;charset=utf-8" });
+        blob.lastModifiedDate = new Date();
+        blob.name = `classroomObservation.txt`;
+        this.uploadFileMixin(blob).then(upload => {
+          resolve({ editorBarData: { type: "0", url: upload.Location, content: tableContent, tableList: tableList } })
+        })
+      })
+
+    },
+    getWavRoleList(params) {
+      return new Promise((resolve, reject) => {
+        this.ajax
+          .post("https://dify.cocorobo.cn/v1/workflows/run?key=role", params)
+          .then(res => {
+            resolve(res);
+          })
+          .catch(err => {
+            console.log("获取说话人身份失败", err)
+            resolve({ data: 1 })
+            // reject(err);
+          });
+      });
+    },
+    getBehavioralCoding(params) {
+      return new Promise((resolve, reject) => {
+        this.ajax
+          .post("https://dify.cocorobo.cn/v1/workflows/run?key=code", params)
+          .then(res => {
+            resolve(res);
+          })
+          .catch(err => {
+            console.log("获取说话人编码失败", err)
+            resolve({ data: 1 })
+          });
+      });
+    },
+    //m4a转wav
+    audioToWavMixin(fileObj) {
+      return new Promise((resolve) => {
+        const audioContext = new (window.AudioContext || window.webkitAudioContext)();
+
+        const reader = new FileReader();
+
+        reader.onload = (e)=>{
+          const arrayBuffer = e.target.result;
+
+          // 解码音频数据
+          audioContext.decodeAudioData(arrayBuffer)
+            .then(audioBuffer => {
+
+              let wavBlob = this.bufferToWav(audioBuffer);
+
+              let _wavFile = new File([wavBlob], "audio.wav", {
+                type: "audio/wav"
+              })
+              // 在控制台输出WAV文件对象
+              resolve({ data: _wavFile })
+              console.log('转换后的WAV文件对象:', _wavFile);
+            })
+            .catch(err => {
+              resolve({ data: 1, err: err })
+            });
+        };
+        reader.onerror = (e) => {
+          resolve({ data: 1, err: e })
+        };
+
+        reader.readAsArrayBuffer(fileObj);
+      })
+    }
+  }
+};

+ 21 - 1
src/components/pages/components/appDialog.vue

@@ -51,6 +51,19 @@
                   :value="item.value"
                 ></el-option>
               </el-select>
+              <el-select
+                v-model="AreaType"
+                placeholder="请选择状态"
+                @change="changeSelectType"
+                style="width: 110px;margin-right: 10px;"
+              >
+                <el-option
+                  v-for="item in AreaSelect"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value"
+                ></el-option>
+              </el-select>
             </div>
 
             <div>
@@ -440,6 +453,12 @@ export default {
         { value: "agent", label: "智能体" },
         { value: "workflow", label: "工作流" }
       ],
+      AreaType:'',
+      AreaSelect: [
+        { value: "cn", label: "cn" },
+        { value: "hk", label: "hk" },
+        { value: "com", label: "com" }
+      ],
       userId: this.$route.query["userid"],
       org: this.$route.query["org"],
       oid: this.$route.query["oid"],
@@ -503,6 +522,7 @@ export default {
       this.type = type;
       const { data } = await this.ajax.get(this.$store.state.api + 'select_oidArea', { oid: this.oid });
       this.region = data[0][0].area;
+      this.AreaType = data[0][0].area;
       this.getAppStoreControl().then(_ => {
         this.getData();
         this.getTypeList();
@@ -614,7 +634,7 @@ export default {
         label: this.selectLabel, //应用的标签搜索
         type: this.showType, //应用的类型
         juri: this.selectJuri, //应用权限 1:我的  2:组织内  3:所有人
-        stand: this.region ? this.region : "cn", //cn站还是hk站
+        stand: this.AreaType ? this.AreaType : "cn", //cn站还是hk站
         status: this.statusType,
         exportType:
           this.controlsObj &&

+ 2 - 1
src/components/pages/newCourse/addCourse.vue

@@ -1020,6 +1020,7 @@
                               "
                               style="margin-top: 20px"
                             >
+                              <div style="margin-bottom: 5px;font-size: 14px;">提示:如输入数学公式需加$符号包裹。例:$a^2 + b^2 = c^2$</div>
                               <editor-bar
                                 class="addEditor"
                                 style="margin: 0"
@@ -18552,7 +18553,7 @@ ol {
 
 .addEditor >>> .text {
   height: auto;
-  min-height: 100px;
+  min-height: 200px;
 }
 
 .addEditor >>> .w-e-text-container {

+ 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>

+ 29 - 2
src/components/pages/test/check/index.vue

@@ -2,8 +2,8 @@
   <div class="pb_content" style="background: #f0f2f5" v-loading="loading">
     <div class="pb_content_body" style="position: relative; margin: 0">
       <div class="right">
-        <div class="courseTop">
-          <div class="stepsNav">
+        <div class="courseTop" >
+          <div class="stepsNav" v-if="!examineId">
             <el-breadcrumb separator-class="el-icon-arrow-right">
               <el-breadcrumb-item
               v-if="!back"
@@ -1379,8 +1379,10 @@
                   <el-button
                     @click="getTest(scope.row)"
                     type="primary"
+                    v-if="(examineData && examineData.type == 1) || !examineData"
                     size="small"
                     >查看</el-button
+
                   >
                   <!-- <el-button @click="setWordHtml(scope.row)" type="primary" size="small">导出答题信息</el-button>
 									<el-button @click="setWordTemplate(scope.row)" type="primary" size="small">word导出</el-button> -->
@@ -1388,6 +1390,7 @@
                     @click="deleteTest(scope.row.id)"
                     type="primary"
                     size="small"
+                     v-if="(examineData && examineData.type == 1) || !examineData"
                     >删除</el-button
                   >
                 </template>
@@ -1904,6 +1907,8 @@ export default {
       peopleId: this.$route.query.peopleId,
       back:this.$route.query.back,
       screenWidth: window.innerWidth,
+      examineId: this.$route.query.examineId,
+      tType: this.$route.query.tType,
       isDesktop: false,
       aiChatV: false,
       title: "",
@@ -1959,6 +1964,7 @@ export default {
       // fileLoadNum: [],
       // infoprogress: [],
       teaType: [],
+      examineData:null,
     };
   },
   watch: {},
@@ -3852,6 +3858,23 @@ ${JSON.stringify(item.array)}
             this.$message.error("生成词云图失败")
           })
 		},
+    getExamineData(){
+      let params = {
+        uid: this.peopleId,
+        type:this.tType,
+        tid:this.examineId,
+      }
+
+      this.ajax.get(this.$store.state.api +"getTestExamineByUserId2",params).then(res=>{
+        // this.examineData = res.data[0][0];
+        if(res.data[0].length>0){
+          this.examineData = res.data[0][0];
+        }
+      }).catch(e=>{
+        console.log(e);
+        this.examineData = null;
+      })
+    },
   },
 
   beforeDestroy() {
@@ -3869,6 +3892,10 @@ ${JSON.stringify(item.array)}
       } else {
         this.getData();
       }
+
+      if(this.examineId){
+        this.getExamineData();
+      }
       // this.getClass2()
       this.changeHeight();
     });

+ 1 - 1
src/components/pages/testPerson/examine/index.vue

@@ -541,7 +541,7 @@ export default {
     // 查看数据来源
     lookPrize(val) {
       // return;
-      this.ifmUrl = `https://beta.pbl.cocorobo.cn/pbl-teacher-table/dist/#/checkToTest?cid=${val}&oid=${this.oid}&org=${this.org}&type=2&role=0&peopleId=${this.userid}`;
+      this.ifmUrl = `https://beta.pbl.cocorobo.cn/pbl-teacher-table/dist/#/checkToTest?cid=${val}&oid=${this.oid}&org=${this.org}&type=2&role=0&peopleId=${this.userid}&tType=${this.pType}&examineId=${this.testExamineBaseList[0].id}`;
       // this.ifmUrl = `https://beta.pbl.cocorobo.cn/pbl-teacher-table/dist/#/test?userid=${this.userid}&oid=45facc0a-1211-11ec-80ad-005056b86db5&org=&role=0`;
       this.diaIframe = true;
     },

Some files were not shown because too many files changed in this diff