Parcourir la source

Merge branch 'beta' into HK

SanHQin il y a 1 mois
Parent
commit
4435ce0ea8
36 fichiers modifiés avec 4800 ajouts et 420 suppressions
  1. 4 0
      dist/index.html
  2. 0 0
      dist/static/css/app.1d3e7674654b241a53a04dce7bde64e7.css.map
  3. 1 0
      dist/static/css/app.d3d61584c9b1ea86f7343bc2e0cff298.css.map
  4. BIN
      dist/static/img/rt-ch_echarts.8000b59.png
  5. 0 0
      dist/static/js/0.4f3b05586c3acc102a54.js
  6. 0 0
      dist/static/js/0.4f3b05586c3acc102a54.js.map
  7. 0 0
      dist/static/js/0.df8814bab917ab2583e0.js.map
  8. 1 0
      dist/static/js/app.cbba84c11efe1ede4b7d.js
  9. 1 0
      dist/static/js/app.cbba84c11efe1ede4b7d.js.map
  10. 1 0
      dist/static/js/app.f224d09377abd98aa9f9.js
  11. 0 0
      dist/static/js/app.f224d09377abd98aa9f9.js.map
  12. 7 0
      dist/static/js/manifest.161e82026ac2ae03ab6f.js
  13. 1 0
      dist/static/js/manifest.161e82026ac2ae03ab6f.js.map
  14. 6 1
      dist/static/js/manifest.9065f91f312a070ed891.js
  15. 0 0
      dist/static/js/manifest.9065f91f312a070ed891.js.map
  16. 3 0
      src/assets/clear.svg
  17. 3 0
      src/assets/delicon.svg
  18. 4 0
      src/assets/icon/classroomObservation/isDoStatus_icon.svg
  19. 10 0
      src/assets/icon/classroomObservation/successStatus_icon.svg
  20. 4 0
      src/assets/icon/classroomObservation/waitStatus_icon.svg
  21. 80 7
      src/components/pages/classroomObservation/components/analysisItem.vue
  22. 203 160
      src/components/pages/classroomObservation/components/analysisSpecialItem.vue
  23. 5 3
      src/components/pages/classroomObservation/components/resourceLibraryDialog.vue
  24. 937 70
      src/components/pages/classroomObservation/dialog/batchCreationClassDialog.vue
  25. 142 49
      src/components/pages/classroomObservation/dialog/editBaseMessageDialog.vue
  26. 161 25
      src/components/pages/classroomObservation/dialog/uploadFileToCreateClassDialog.vue
  27. 72 52
      src/components/pages/classroomObservation/index.vue
  28. 199 29
      src/components/pages/classroomObservation/newComponents/batchClassCard.vue
  29. 2246 0
      src/components/pages/classroomObservation/tools/mixin.js
  30. 21 1
      src/components/pages/components/appDialog.vue
  31. 2 1
      src/components/pages/newCourse/addCourse.vue
  32. 27 6
      src/components/pages/sassPlatform/index.vue
  33. 2 2
      src/components/pages/sz/dataBoardArea/dataCenter/chartList/courseRank/index.vue
  34. 2 2
      src/components/pages/sz/dataBoardCity/dataCenter/chartList/courseRank/index.vue
  35. 654 11
      src/components/pages/test/check/index.vue
  36. 1 1
      src/components/pages/testPerson/examine/index.vue

+ 4 - 0
dist/index.html

@@ -32,7 +32,11 @@
       width: 100%;
       background: #e6eaf0;
       font-family: '黑体';
+<<<<<<< HEAD
     }</style><link href=./static/css/app.1d3e7674654b241a53a04dce7bde64e7.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.9065f91f312a070ed891.js></script><script type=text/javascript src=./static/js/vendor.bb486323f0fa002ba2e7.js></script><script type=text/javascript src=./static/js/app.f224d09377abd98aa9f9.js></script></body></html><script>function stopSafari() {
+=======
+    }</style><link href=./static/css/app.d3d61584c9b1ea86f7343bc2e0cff298.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.cbba84c11efe1ede4b7d.js></script></body></html><script>function stopSafari() {
+>>>>>>> beta
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/static/css/app.1d3e7674654b241a53a04dce7bde64e7.css.map


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
dist/static/css/app.d3d61584c9b1ea86f7343bc2e0cff298.css.map


BIN
dist/static/img/rt-ch_echarts.8000b59.png


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/static/js/0.4f3b05586c3acc102a54.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/static/js/0.4f3b05586c3acc102a54.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/static/js/0.df8814bab917ab2583e0.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
dist/static/js/app.cbba84c11efe1ede4b7d.js


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
dist/static/js/app.cbba84c11efe1ede4b7d.js.map


Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
dist/static/js/app.f224d09377abd98aa9f9.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/static/js/app.f224d09377abd98aa9f9.js.map


+ 7 - 0
dist/static/js/manifest.161e82026ac2ae03ab6f.js

@@ -0,0 +1,7 @@
+<<<<<<<< HEAD:dist/static/js/manifest.9065f91f312a070ed891.js
+!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:"da6498e5ed226bc25eb5",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.9065f91f312a070ed891.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
+>>>>>>>> beta:dist/static/js/manifest.161e82026ac2ae03ab6f.js

Fichier diff supprimé car celui-ci est trop grand
+ 1 - 0
dist/static/js/manifest.161e82026ac2ae03ab6f.js.map


+ 6 - 1
dist/static/js/manifest.9065f91f312a070ed891.js

@@ -1,2 +1,7 @@
+<<<<<<<< HEAD:dist/static/js/manifest.9065f91f312a070ed891.js
 !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:"da6498e5ed226bc25eb5",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.9065f91f312a070ed891.js.map
+//# sourceMappingURL=manifest.9065f91f312a070ed891.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
+>>>>>>>> beta:dist/static/js/manifest.161e82026ac2ae03ab6f.js

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/static/js/manifest.9065f91f312a070ed891.js.map


+ 3 - 0
src/assets/clear.svg

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7 4H9V2H7V4ZM10 2V4H13C13.5523 4 14 4.44772 14 5V7C14 7.50427 13.6267 7.92137 13.1414 7.99007L13.8369 12.8586C13.923 13.461 13.4555 14 12.847 14H3.15301C2.54446 14 2.077 13.461 2.16306 12.8586L2.85856 7.99007C2.37326 7.92137 2 7.50427 2 7V5C2 4.44772 2.44772 4 3 4H6V2C6 1.44772 6.44772 1 7 1H9C9.55228 1 10 1.44772 10 2ZM12.1327 7H13V5H9H7H3L3 7H3.8673H12.1327ZM12.1327 8H3.8673L3.15301 13H5V11H6V13H7.5V11H8.5V13H10V11H11V13H12.847L12.1327 8Z" fill="black" fill-opacity="0.9"/>
+</svg>

+ 3 - 0
src/assets/delicon.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10 19.7456C14.8325 19.7456 18.75 15.8281 18.75 10.9956C18.75 6.16307 14.8325 2.24556 10 2.24556C5.16751 2.24556 1.25 6.16307 1.25 10.9956C1.25 15.8281 5.16751 19.7456 10 19.7456ZM9.24988 5.99556H10.7498V7.4955H9.24988V5.99556ZM9.38202 9.12056H10.632V15.9953H9.38202V9.12056Z" fill="#D54941"/>
+</svg>

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

Fichier diff supprimé car celui-ci est trop grand
+ 937 - 70
src/components/pages/classroomObservation/dialog/batchCreationClassDialog.vue


+ 142 - 49
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>
@@ -279,7 +279,7 @@
 									 v-loading="uploadNephogramLoading"
 									v-if="(data.imageList.NephogramList&&data.imageList.NephogramList.length>0)"
 								>
-									<wordcloudEChart :data="imageList.NephogramList[0]"/>
+									<wordcloudEChart :data="data.imageList.NephogramList[0]"/>
 									<span @click.stop="delNephogram('NephogramList')"></span>
 								</div>
 							<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)

+ 161 - 25
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>
@@ -75,12 +75,16 @@
                   />
                 </div>
               </div>
+              <div class="fl_item addFile" v-if="fileList.length > 0" @click="addFile()">
+              <span>继续添加文件</span>
             </div>
-            <div class="b_m_l_noFile" v-else @click="addFile()">
+            </div>
+
+            <div class="b_m_l_noFile" v-if="fileList.length==0" @click="addFile()">
               <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 +211,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");
@@ -223,50 +228,88 @@ export default {
             file: uploadingFile.file
           });
         });
+      }else if(this.fileList.find(file => file.status === "uploading")){
+        console.log("还有在上传的文件")
       } else {
         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;
         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 = new File([files[i]], files[i].name.replace(/\s+/g, ''), {type: files[i].type});
+            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")) {
-          let uploadingFile = this.fileList.find(
-            file => file.status === "wait"
-          );
-          if (uploadingFile) {
-            this.fileList.find(file => file.status === "wait").status =
-              "uploading";
-            this.$nextTick(() => {
-              this.$refs[`uploadFileRef_${uploadingFile.index}`][0].awsupload({
-                file: uploadingFile.file
+          let uploadingFile = this.fileList.filter(file => file.status === "wait").slice(0, 3);
+          if(uploadingFile.length>0){
+            uploadingFile.forEach(i=>{
+              this.fileList.find(file => i.index===file.index).status = "uploading";
+              this.$nextTick(() => {
+              this.$refs[`uploadFileRef_${i.index}`][0].awsupload({
+                file: i.file
               });
             });
+            })
+          }
+          // let uploadingFile = this.fileList.find(
+          //   file => file.status === "wait"
+          // );
+          // if (uploadingFile) {
+          //   this.fileList.find(file => file.status === "wait").status =
+          //     "uploading";
+          //   this.$nextTick(() => {
+          //     this.$refs[`uploadFileRef_${uploadingFile.index}`][0].awsupload({
+          //       file: uploadingFile.file
+          //     });
+          //   });
+          // }
+        }
+
+        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;
+            }
           }
         }
       });
@@ -306,7 +349,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>
@@ -544,4 +666,18 @@ export default {
 .uploadingProgress {
   background-color: rgba(54, 129, 252, 1);
 }
+
+.addFile{
+  width: 95%;
+  min-height: 45px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+  margin-top: 10px;
+  box-sizing: border-box;
+  border: dashed  1px #969BA3;
+  border-radius: 10px;
+  margin:0 auto;
+}
 </style>

+ 72 - 52
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",
@@ -502,6 +504,7 @@ export default {
         tagList.forEach(i => (i.dataList = []));
         let url = `https://cloud.cocorobo.hk/aigpt/#/classroom_observation_board?tid=${this.tid}`;
         const qRCodeSrc = await this.getQrCodeImageSrc(url);
+        dataList.sort((a, b) => a.tIndex - b.tIndex);
         dataList.forEach(i1 => {
           tagList.forEach(i2 => {
             if (i2.value == i1.Type) {
@@ -510,6 +513,9 @@ export default {
           });
         });
 
+
+
+
         let directoryHtml = `<div style="margin-bottom:1in"><div style="text-align:center;font-size:20pt;margin-bottom:0.5in">目录</div>`;
 
         let analysisHtml = ``;
@@ -548,20 +554,20 @@ export default {
               tagHtml += `<p style="font-size:10.5pt;font-style:italic;margin-bottom:-0.7in;color:#6b798e">${i2.jsonData.result}</p>`;
             }
             if (i2.jsonData.eChartData) {
-              tagHtml += `<img src="${await this.getEChartsImageSrc(
+              tagHtml += `<div style="width:100vw;padding:70%;box-sizing: border-box;text-align:center"><img style="margin:auto" src="${await this.getEChartsImageSrc(
                 i2.jsonData.eChartData
-              )}"/>`;
+              )}"/></div>`;
             }
 
             if (i2.jsonData.spectrogramData) {
-              tagHtml += `<img src="${await this.getEChartsSpectrogramImage(
+              tagHtml += `<div style="width:100vw;padding:70%;box-sizing: border-box;text-align:center"><img style="margin:auto" src="${await this.getEChartsSpectrogramImage(
                 i2.jsonData.spectrogramData
-              )}"/>`;
+              )}"/></div>`;
               // console.log()
             }
 
             if (i2.jsonData.CH && i2.jsonData.RT) {
-              tagHtml += `<div style="width:100vw;text-align:center;"><img style="margin:auto" src="${await this.getEChartsechartsRTCHImage(
+              tagHtml += `<div style="width:100vw;text-align:center;padding:70%;box-sizing: border-box;"><img style="margin:auto" src="${await this.getEChartsechartsRTCHImage(
                 {
                   RT: i2.jsonData.RT,
                   CH: i2.jsonData.CH
@@ -569,8 +575,7 @@ export default {
               )}"/></div>`;
             }
 
-            let _content = md.render(i2.jsonData.content);
-
+            let _content = md.render(i2.jsonData.content).replace(/<p>/g, '').replace(/<\/p>/g, '').replace(/<strong>/g, '<span style="font-weight: bold;">').replace(/<\/strong>/g, '</span>');
             tagHtml += `<p style="font-size:10.5pt;margin-bottom:-0.5in">${_content}</p>`;
           }
           tagHtml += "</div>";
@@ -613,6 +618,8 @@ export default {
 				${analysisHtml}
 			</div>
 			`;
+      // return console.log(analysisHtml.replace(/<img.*?>/g, ''))
+
         this.generateDocx(`《${bmData.courseName}》课堂观察报告`, _html);
         this.loading = false;
       } catch (e) {
@@ -640,8 +647,8 @@ export default {
     getEChartsImageSrc(option) {
       return new Promise(resolve => {
         let hiddenDiv = document.createElement("div");
-        hiddenDiv.style.width = "600px";
-        hiddenDiv.style.height = "500px";
+        hiddenDiv.style.width = "400px";
+        hiddenDiv.style.height = "400px";
         hiddenDiv.style.position = "absolute";
         hiddenDiv.style.left = "-9999px"; // 隐藏div
         document.body.appendChild(hiddenDiv);
@@ -656,7 +663,7 @@ export default {
           // 获取图表的图片
           let base64Image = myChart.getDataURL({
             type: "png", // 图片格式
-            pixelRatio: 1, // 图像清晰度
+            pixelRatio: 0.9, // 图像清晰度
             backgroundColor: "#fff" // 背景颜色
           });
 
@@ -735,21 +742,23 @@ export default {
           });
 
           // 绘制红色垂直线(指定位置)
-          ctx.strokeStyle = "red";
-          ctx.lineWidth = 2;
-
-          data.breakpoint.forEach(i => {
-            const breakpointPo = parseFloat(
-              (i / (sum / canvasWidth2)).toFixed(2)
-            );
-            ctx.beginPath();
-            ctx.moveTo(breakpointPo, 10);
-            ctx.lineTo(breakpointPo, canvasHeight - 70);
-            ctx.stroke();
-          });
+          // ctx.strokeStyle = "red";
+          // ctx.lineWidth = 2;
+
+          // data.breakpoint.forEach(i => {
+          //   const breakpointPo = parseFloat(
+          //     (i / (sum / canvasWidth2)).toFixed(2)
+          //   );
+          //   ctx.beginPath();
+          //   ctx.moveTo(breakpointPo, 10);
+          //   ctx.lineTo(breakpointPo, canvasHeight - 70);
+          //   ctx.stroke();
+          // });
 
           let interval = parseFloat((300 / (sum / canvasWidth2)).toFixed(2));
           //绘制竖线
+          let _lastI = 0;
+          //绘制竖线
           for (let i = 0; i < canvasWidth2; i += interval) {
             ctx.beginPath();
             ctx.strokeStyle = "#BFBFBF";
@@ -761,11 +770,23 @@ export default {
             ctx.font = `${fontSize}px serif`;
             if (i == 0) {
               ctx.fillText(`${timeLabel}min`, i + 10, canvasHeight - 40);
-            } else if (i + interval > canvasWidth2) {
+            } else if (i + interval >= canvasWidth2) {
               ctx.fillText(`${timeLabel}min`, i - 20, canvasHeight - 40);
             } else {
               ctx.fillText(`${timeLabel}min`, i - 15, canvasHeight - 40);
             }
+            _lastI = i;
+          }
+          if (canvasWidth2 - _lastI >60) {
+            ctx.beginPath();
+            ctx.strokeStyle = "#BFBFBF";
+            ctx.moveTo(canvasWidth2 + 10, canvasHeight - 70);
+            ctx.lineTo(canvasWidth2 + 10, canvasHeight - 55);
+            ctx.stroke();
+            ctx.fillStyle = "#868686";
+            let timeLabel = (sum / 60).toFixed(0); // 时间标识计算
+            ctx.font = `${fontSize}px serif`;
+            ctx.fillText(`${timeLabel}min`, canvasWidth2 - 20, canvasHeight - 40);
           }
 
           ctx.beginPath();
@@ -799,33 +820,21 @@ export default {
           ctx.imageSmoothingEnabled = false;
           ctx.lineWidth = 1;
           const img = new Image();
-          img.src = require("../../../assets/icon/classroomObservation/rt-ch_echarts.png");
+          img.src = require("../../../assets/icon/classroomObservation/rt-ch_echarts2.svg");  //ch_echarts2
           img.onload = () => {
-            ctx.drawImage(img, 25, 25, 250, 250);
-            ctx.fillStyle = fontColor;
+            ctx.drawImage(img, 0, 0, canvasWidth, canvasWidth);
             ctx.beginPath();
-            ctx.arc(
-              250 * parseFloat(data.RT) + 25,
-              250 - 250 * parseFloat(data.CH) + 25,
-              4,
-              0,
-              2 * Math.PI
-            );
+            let _showWidth = canvasWidth-((canvasWidth/8.8)*2)
+            ctx.arc((canvasWidth/8.8)+(_showWidth*parseFloat(data.RT)),(canvasWidth/8.8)+_showWidth-(_showWidth*parseFloat(data.CH)),4,0,2*Math.PI);
+            ctx.lineWidth = 0.5; // 设置边框大小
+            // ctx.arc((canvasWidth*parseFloat(this.data.RT))+(canvasWidth/8.8),(canvasWidth-(canvasWidth*parseFloat(this.data.CH))+(canvasWidth/8.8)), 4, 0, 2 * Math.PI);
             ctx.fill();
             ctx.stroke();
-            ctx.fillStyle = "black";
-            ctx.font = `20px serif`;
-            ctx.fillText("1", 10, 40);
-            ctx.fillText("Ch", 0, 150);
-            ctx.fillText("0", 10, 290);
-            ctx.fillText("Rt", 140, 295);
-            ctx.fillText("1", 280, 275);
-
             ctx.fillStyle = fontColor;
             ctx.font = "italic bold 24px Arial";
-            ctx.fillText(`RT=${data.RT}    CH=${data.CH}`, 40, 330);
-
-            // 将 canvas 转换为 base64 格式的图片地址
+            const text = `RT=${data.RT}    CH=${data.CH}`;
+            const textWidth = ctx.measureText(text).width;
+            ctx.fillText(text, (canvasWidth - textWidth) / 2, canvasHeight - canvasWidth / 12);
             const base64Image = canvas.toDataURL("image/png");
             resolve(base64Image);
           };
@@ -896,11 +905,21 @@ export default {
 							margin-right:-1in;
 						}
 						li{
-							margin-bottom:0.1in
-							margin-right:-1in;
+							position: relative;
+              padding-left: 1.5em; /* 控制项目符号与文本的距离 */
+              margin-bottom: 0.5em;
+              text-indent: -1.5em; /* 负缩进使文本与符号对齐 */
+              mso-special-format: bullet;
+              margin-left: 0;
+              padding-left: 10pt;
+              text-indent: -10pt;
+              mso-style-name: "Normal";
+              mso-style-priority: 99;
+              mso-style-unhide: no;
+              mso-style-qformat: yes;
+              mso-style-parent: "";
 						}
 						p{
-							line-height:1;
 							margin:0;
 							padding:0
 						}
@@ -910,8 +929,6 @@ export default {
       ${html}
       </body>
       </html>`;
-      // console.log(content)
-      // return console.log(content)
       // debugger
       let blob = htmlDocx.asBlob(content);
 
@@ -1214,6 +1231,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>

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

@@ -0,0 +1,2246 @@
+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";
+
+import QRCode from "qrcodejs2";
+import * as echarts from "echarts";
+import "echarts-wordcloud";
+// word
+import htmlDocx from "html-docx-js/dist/html-docx";
+
+export const toolMixin = {
+  data(){
+    return{
+      tag: {
+        0: "一",
+        1: "二",
+        2: "三",
+        3: "四",
+        4: "五",
+        5: "六",
+        6: "七",
+        7: "八",
+        8: "九",
+        9: "十",
+        10: "十一",
+        11: "十二",
+        12: "十三",
+        13: "十四",
+        14: "十五",
+        15: "十六",
+        16: "十七",
+        17: "十八",
+        18: "十九",
+        19: "二十"
+      }
+    }
+  },
+  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)
+              console.log("管谱图数据",resultData)
+              if (resultData.message) analysisData.content = resultData.message;
+              if (resultData.eCharts) analysisData.spectrogramData = 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 {
+          let _msg = assistant.tips;
+          let message = await this.getAiContentMixin({ _msg: _msg, fileId: fileId })
+          this.getContentTableMixin(message.data).then(async res => {
+            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(!item.role)return;
+              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;
+
+
+
+            if (message.data == 1) {
+              return resolve({ eCharts: _data, message: "" })
+            } else {
+              return resolve({ eCharts: _data, message: message.data })
+            }
+            // spectrogramData  _data
+
+
+          });
+        } catch (e) {
+          console.log("光谱生成失败",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 } })
+        })
+      })
+
+    },
+    //文本的自动编码
+    automaticCodingForTextMixin(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 == '' &&  i.value != "" && i.index !=="")) {
+          let _ajaxList = tableList.filter(i => 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;
+
+          _ajaxList.forEach((item, index) => {
+            let _findIndex = tableList.findIndex(i => i.index === item.index);
+            if (_findIndex != -1) {
+              tableList[_findIndex].role = _roleResult[index];
+            }
+          })
+          // 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("allRole",tableList.map(i=>i.role))
+
+        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) => {
+          if(item.value=="")return;
+          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);
+      })
+    },
+    getQrCodeImageSrc(url) {
+      return new Promise((resolve, reject) => {
+        let qrcode = new QRCode(document.createElement("div"), {
+          text: url, // 需要转换为二维码的内容
+          width: 150,
+          height: 150,
+          colorDark: "#000000",
+          colorLight: "#ffffff",
+          correctLevel: QRCode.CorrectLevel.H
+        });
+        let img = qrcode._el.getElementsByTagName("img")[0];
+        img.onload = () => {
+          resolve(img.src);
+        };
+      });
+    },
+    getEChartsImageSrc(option) {
+      return new Promise(resolve => {
+        try {
+          let hiddenDiv = document.createElement("div");
+        hiddenDiv.style.width = "400px";
+        hiddenDiv.style.height = "400px";
+        hiddenDiv.style.position = "absolute";
+        hiddenDiv.style.left = "-9999px"; // 隐藏div
+        document.body.appendChild(hiddenDiv);
+
+        // 初始化图表
+        let myChart = echarts.init(hiddenDiv);
+
+       console.log(option)
+
+       let time;
+        myChart.on("rendered", async () => {
+          // console.log("生成echarts成功")
+          // 获取图表的图片
+
+          clearTimeout(time)
+          time = setTimeout(()=>{
+            let base64Image = myChart.getDataURL({
+              type: "png", // 图片格式
+              pixelRatio: 0.9, // 图像清晰度
+              backgroundColor: "#fff" // 背景颜色
+            });
+
+            resolve(base64Image);
+            // 清除隐藏的div和图表实例
+            document.body.removeChild(hiddenDiv);
+            myChart.dispose();
+          },200)
+
+        }).on("error", (error) => {
+          console.log("生成echarts失败",error)
+          resolve("#")
+        });
+
+        // 设置图标配置
+        myChart.setOption(option);
+          // console.log("词云图???",option)
+
+        } catch (error) {
+          console.log(error,"error")
+          resolve("#")
+        }
+      });
+    },
+    getImageSrcToBase64(src) {
+      return new Promise((resolve, reject) => {
+        const image = new Image();
+        image.src = src;
+        image.onload = () => {
+          const canvas = document.createElement("canvas");
+          canvas.width = image.naturalWidth;
+          canvas.height = image.naturalHeight;
+          canvas.style.width = `${canvas.width / window.devicePixelRatio}px`;
+          canvas.style.height = `${canvas.height / window.devicePixelRatio}px`;
+
+          const context = canvas.getContext("2d");
+          context.drawImage(image, 0, 0);
+          const base64 = canvas.toDataURL("image/png");
+          resolve(base64);
+        };
+      });
+    },
+    getEChartsSpectrogramImage(data) {
+      return new Promise(resolve => {
+        try {
+          let canvas = document.createElement("canvas");
+          let ctx = canvas.getContext("2d");
+          canvas.width = 600 * window.devicePixelRatio;
+          canvas.height = 200 * window.devicePixelRatio;
+          // 缩放绘图上下文
+          ctx.scale(1, 1);
+
+          let canvasWidth = canvas.width;
+          let canvasWidth2 = canvasWidth - 20;
+          let canvasHeight = canvas.height;
+          ctx.imageSmoothingEnabled = false;
+          ctx.lineWidth = 1;
+
+          // 设置颜色和文字
+          const teacherColor = "#5470C6"; // 老师的颜色
+          const studentColor = "#91CC75"; // 学生的颜色
+          const fontSize = 14; //字体大小
+
+          ctx.fillStyle = teacherColor;
+          this.drawRoundedRect(ctx, 0, canvasHeight - 20, 20, 15, 4);
+          // ctx.fillRect(0, canvasHeight - 20, 25, 20);
+          ctx.fillStyle = "black";
+          ctx.font = `${fontSize}px serif`;
+          ctx.fillText("老师", 28, canvasHeight - 7);
+
+          ctx.fillStyle = studentColor;
+          // ctx.fillRect(100, canvasHeight - 20, 25, 20);
+          this.drawRoundedRect(ctx, 100, canvasHeight - 20, 20, 15, 4);
+          ctx.fillStyle = "black";
+          ctx.font = `${fontSize}px serif`;
+          ctx.fillText("学生", 128, canvasHeight - 7);
+          let sum = data.data.reduce((pre, cur) => (pre += cur.value), 0);
+          // 当前x位置的起始点
+          let currentX = 10;
+          // 计算并绘制每个区域
+          data.data.forEach(i => {
+            const segmentWidth = parseFloat(
+              (i.value / (sum / canvasWidth2)).toFixed(2)
+            );
+            ctx.fillStyle = i.type == 0 ? teacherColor : studentColor;
+            ctx.fillRect(currentX, 20, segmentWidth, canvasHeight - 100);
+
+            // 更新x位置
+            currentX += segmentWidth;
+          });
+
+          // 绘制红色垂直线(指定位置)
+          // ctx.strokeStyle = "red";
+          // ctx.lineWidth = 2;
+
+          // data.breakpoint.forEach(i => {
+          //   const breakpointPo = parseFloat(
+          //     (i / (sum / canvasWidth2)).toFixed(2)
+          //   );
+          //   ctx.beginPath();
+          //   ctx.moveTo(breakpointPo, 10);
+          //   ctx.lineTo(breakpointPo, canvasHeight - 70);
+          //   ctx.stroke();
+          // });
+
+          let interval = parseFloat((300 / (sum / canvasWidth2)).toFixed(2));
+          //绘制竖线
+          let _lastI = 0;
+          //绘制竖线
+          for (let i = 0; i < canvasWidth2; i += interval) {
+            ctx.beginPath();
+            ctx.strokeStyle = "#BFBFBF";
+            ctx.moveTo(i == 0 ? 10 : i, canvasHeight - 70);
+            ctx.lineTo(i == 0 ? 10 : i, canvasHeight - 55);
+            ctx.stroke();
+            ctx.fillStyle = "#868686";
+            let timeLabel = (((i / canvasWidth2) * sum) / 60).toFixed(0); // 时间标识计算
+            ctx.font = `${fontSize}px serif`;
+            if (i == 0) {
+              ctx.fillText(`${timeLabel}min`, i + 10, canvasHeight - 40);
+            } else if (i + interval >= canvasWidth2) {
+              ctx.fillText(`${timeLabel}min`, i - 20, canvasHeight - 40);
+            } else {
+              ctx.fillText(`${timeLabel}min`, i - 15, canvasHeight - 40);
+            }
+            _lastI = i;
+          }
+          if (canvasWidth2 - _lastI >60) {
+            ctx.beginPath();
+            ctx.strokeStyle = "#BFBFBF";
+            ctx.moveTo(canvasWidth2 + 10, canvasHeight - 70);
+            ctx.lineTo(canvasWidth2 + 10, canvasHeight - 55);
+            ctx.stroke();
+            ctx.fillStyle = "#868686";
+            let timeLabel = (sum / 60).toFixed(0); // 时间标识计算
+            ctx.font = `${fontSize}px serif`;
+            ctx.fillText(`${timeLabel}min`, canvasWidth2 - 20, canvasHeight - 40);
+          }
+
+          ctx.beginPath();
+          ctx.strokeStyle = "#BFBFBF";
+          ctx.moveTo(10, canvasHeight - 55);
+          ctx.lineTo(canvasWidth2 + 10, canvasHeight - 55);
+          ctx.stroke();
+
+          // 将 canvas 转换为 base64 格式的图片地址
+          const base64Image = canvas.toDataURL("image/png");
+          resolve(base64Image);
+        } catch (e) {
+          console.log(e);
+          resolve("#");
+        }
+      });
+    },
+    getEChartsechartsRTCHImage(data) {
+      return new Promise(resolve => {
+        try {
+          let canvas = document.createElement("canvas");
+          let ctx = canvas.getContext("2d");
+          canvas.width = 300 * window.devicePixelRatio;
+          canvas.height = 350 * window.devicePixelRatio;
+          // 缩放绘图上下文
+          ctx.scale(1, 1);
+
+          let canvasWidth = canvas.width;
+          let canvasHeight = canvas.height;
+          let fontColor = "#1a7ad3";
+          ctx.imageSmoothingEnabled = false;
+          ctx.lineWidth = 1;
+          const img = new Image();
+          img.src = require("../../../../assets/icon/classroomObservation/rt-ch_echarts2.svg");  //ch_echarts2
+          img.onload = () => {
+            ctx.drawImage(img, 0, 0, canvasWidth, canvasWidth);
+            ctx.beginPath();
+            let _showWidth = canvasWidth-((canvasWidth/8.8)*2)
+            ctx.fillStyle = fontColor;
+            ctx.arc((canvasWidth/8.8)+(_showWidth*parseFloat(data.RT)),(canvasWidth/8.8)+_showWidth-(_showWidth*parseFloat(data.CH)),4,0,2*Math.PI);
+            ctx.lineWidth = 0.5; // 设置边框大小
+            // ctx.arc((canvasWidth*parseFloat(this.data.RT))+(canvasWidth/8.8),(canvasWidth-(canvasWidth*parseFloat(this.data.CH))+(canvasWidth/8.8)), 4, 0, 2 * Math.PI);
+            ctx.fill();
+            ctx.stroke();
+            ctx.fillStyle = fontColor;
+            ctx.font = "italic bold 24px Arial";
+            const text = `RT=${data.RT}    CH=${data.CH}`;
+            const textWidth = ctx.measureText(text).width;
+            ctx.fillText(text, (canvasWidth - textWidth) / 2, canvasHeight - canvasWidth / 12);
+            const base64Image = canvas.toDataURL("image/png");
+            resolve(base64Image);
+          };
+        } catch (e) {
+          console.log(e);
+          resolve("#");
+        }
+      });
+    },
+    drawRoundedRect(ctx, x, y, width, height, radius) {
+      // 限制 radius 的最大值,防止它超过矩形的宽度或高度的一半
+      const actualRadius = Math.min(radius, width / 2, height / 2);
+
+      ctx.beginPath();
+      ctx.moveTo(x + actualRadius, y); // 起点,矩形顶部的左侧
+
+      // 右上角的弧线
+      ctx.arcTo(x + width, y, x + width, y + height, actualRadius);
+
+      // 右下角的弧线
+      ctx.arcTo(x + width, y + height, x, y + height, actualRadius);
+
+      // 左下角的弧线
+      ctx.arcTo(x, y + height, x, y, actualRadius);
+
+      // 左上角的弧线
+      ctx.arcTo(x, y, x + width, y, actualRadius);
+
+      ctx.closePath();
+      ctx.fill(); // 填充颜色
+    },
+    getDocFnPromise(task){
+      console.log("处理👉",task)
+      return new Promise(async (resolve)=>{
+        try {
+          let bmData = task.jsonData.baseMessage;
+        const md = new markdownIt();
+        let dataList = task.jsonData.analysisList;
+        let tagList = task.jsonData.tagList?task.jsonData.tagList:[
+          { value: 0, name: "通用课堂分析", loading: false },
+          { value: 1, name: "学科课堂分析", loading: false },
+          { value: 2, name: "扩展分析", loading: false }
+        ];
+        let showBrief = true;
+
+        // console.log(tagList,"tagList")
+
+        tagList.forEach(i => (i.dataList = []));
+        let url = `https://beta.cloud.cocorobo.cn/aigpt/#/classroom_observation_board?tid=${task.jsonData.createId}`;
+        const qRCodeSrc = await this.getQrCodeImageSrc(url);
+
+        dataList.sort((a, b) => a.tIndex - b.tIndex);
+        dataList.forEach(i1 => {
+          tagList.forEach(i2 => {
+            if (i2.value == i1.Type) {
+              i2.dataList.push(i1);
+            }
+          });
+        });
+
+        let directoryHtml = `<div style="margin-bottom:1in"><div style="text-align:center;font-size:20pt;margin-bottom:0.5in">目录</div>`;
+
+        let analysisHtml = ``;
+
+        // console.log("开始处理文件")
+        for (let c = 0; c < tagList.length; c++) {
+          // console.log(tagList[c],"tagList[c]")
+          let i = tagList[c];
+          let dire = `<div>`;
+          let tagHtml = `<div style="margin-bottom:0.5in">`;
+          if (i.value == 0) {
+            i.dataList = i.dataList.filter(i2 => i2.tIndex != 2);
+          }
+          i.dataList.sort((a, b) => a.tIndex - b.tIndex);
+          tagHtml += `<h1 style="font-size:16pt;margin-bottom:-1in">${
+            this.tag[i.value]
+          }、${i.name}</h1>`;
+          dire += `<p style="font-size:14pt;margin-bottom:-0.8in">${
+            this.tag[i.value]
+          }、${i.name}</p>`;
+
+          for (let d = 0; d < i.dataList.length; d++) {
+            let i2 = i.dataList[d];
+            // console.log(i.dataList[d],"i.dataList[d]")
+            let i2Index = d;
+            tagHtml += `<h2 style="font-size:14pt;margin-bottom:-1in">${i2Index +
+              1}、${
+              i2.jsonData.anotherName
+                ? i2.jsonData.anotherName
+                : i2.jsonData.name
+            }</h2>`;
+            dire += `<p style="font-size:11pt;margin-bottom:-0.8in;margin-left:0.1in">${i2Index +
+              1}、${
+              i2.jsonData.anotherName
+                ? i2.jsonData.anotherName
+                : i2.jsonData.name
+            }</p>`;
+            if (showBrief && i2.jsonData.result) {
+              tagHtml += `<p style="font-size:10.5pt;font-style:italic;margin-bottom:-0.7in;color:#6b798e">${i2.jsonData.result}</p>`;
+            }
+            if (i2.jsonData.eChartData) {
+              // console.log("处理i2.jsonData.eChartData",i2.jsonData)
+              tagHtml += `<div style="width:100vw;padding:70%;box-sizing: border-box;text-align:center"><img style="margin:auto" src="${await this.getEChartsImageSrc(
+                i2.jsonData.eChartData
+              )}"/></div>`;
+            }
+
+            if (i2.jsonData.spectrogramData) {
+              // console.log("处理i2.jsonData.spectrogramData",i2.jsonData)
+              tagHtml += `<div style="width:100vw;padding:70%;box-sizing: border-box;text-align:center"><img style="margin:auto" src="${await this.getEChartsSpectrogramImage(
+                i2.jsonData.spectrogramData
+              )}"/></div>`;
+              // console.log()
+            }
+
+            if (i2.jsonData.CH && i2.jsonData.RT) {
+              // console.log("处理i2.jsonData.CHRT",i2.jsonData)
+              tagHtml += `<div style="width:100vw;text-align:center;padding:70%;box-sizing: border-box;"><img style="margin:auto" src="${await this.getEChartsechartsRTCHImage(
+                {
+                  RT: i2.jsonData.RT,
+                  CH: i2.jsonData.CH
+                }
+              )}"/></div>`;
+            }
+
+            let _content = md.render(i2.jsonData.content).replace(/<p>/g, '').replace(/<\/p>/g, '').replace(/<strong>/g, '<span style="font-weight: bold;">').replace(/<\/strong>/g, '</span>');
+            tagHtml += `<p style="font-size:10.5pt;margin-bottom:-0.5in">${_content}</p>`;
+          }
+          tagHtml += "</div>";
+          dire += "</div>";
+          analysisHtml += tagHtml;
+          directoryHtml += dire;
+        }
+
+        // console.log("处理分析完成")
+        directoryHtml += "</div>";
+        let _html = `
+			      <div>
+			      	<p style="width:100vw;margin-bottom:1.5in">*分析结果仅供参考</p>
+			      	<p style="font-size:28pt;width:100vw;text-align:center;">课堂观察报告</p>
+			      	<p style="font-size:10pt;width:100vw;text-align:center;margin-bottom:0.6in">报告生成时间:${new Date().toLocaleString()}</p>
+			      	<div style="font-size:16pt;width:100vw;text-align:center;margin-bottom:1in">
+			      		<p style="font-size:20pt;margin-bottom:0.7in">《${bmData.courseName}》</p>
+			      		<p style="margin-bottom:-1in">授课老师:${
+                  bmData.teacherName ? bmData.teacherName : "未填写"
+                }</p>
+			      		<p style="margin-bottom:-1in">授课年级:${
+                  bmData.grade ? bmData.grade : "未填写"
+                }</p>
+			      		<p style="margin-bottom:-1in">授课科目:${
+                  bmData.subject ? bmData.subject : "未填写"
+                }</p>
+			      		<p style="margin-bottom:-1in">授课时间:${
+                  bmData.time ? bmData.time : "未填写"
+                }</p>
+			      	</div>
+			      	<div style="font-size:16pt;width:100vw;text-align:center;margin-bottom:0.5in">
+			      		<img src="${qRCodeSrc}" style="width:150px;height:150px;margin:auto;"/>
+			      		<p>扫码查看网页版</p>
+			      	</div>
+			      </div>
+			      ${directoryHtml}
+
+			      <div>
+			      	${analysisHtml}
+			      </div>
+			      `;
+
+            const content = `<!DOCTYPE html>
+            <html xmlns:v='urn:schemas-microsoft-com
+            :vml'xmlns:o='urn:schemas-microsoft-com:office
+            :office'xmlns:w='urn:schemas-microsoft-com:office
+            :word'xmlns:m='http://schemas.microsoft.com/office/2004/12/omml'
+            xmlns='http://www.w3.org/TR/REC-html40'
+            xmlns='http://www.w3.org/1999/xhtml'>
+            <head>
+                <meta charset="UTF-8">
+                <meta http-equiv="X-UA-Compatible" content="IE=edge">
+                <meta name="viewport" content="width=device-width, initial-scale=1.0">
+                <title>《${bmData.courseName}》课堂观察报告</title>
+                <style>
+			      			*{
+			      				font-family: '宋体';
+			      				margin:0;
+			      				padding:0;
+			      				line-height:1;
+			      			}
+                  table {
+                    border-collapse: collapse; /* 折叠边框 */
+                    width: 100%;
+			      				font-size:10.5pt;
+                  }
+                  th, td {
+                    border: 1px solid black; /* 线条样式 */
+                    padding: 8px;
+                    text-align: left;
+			      				font-size:10.5pt;
+                  }
+			      			ol,ul{
+			      				margin:0;
+			      				padding:0;
+			      				margin-right:-1in;
+			      			}
+			      			li{
+			      				position: relative;
+                    padding-left: 1.5em; /* 控制项目符号与文本的距离 */
+                    margin-bottom: 0.5em;
+                    text-indent: -1.5em; /* 负缩进使文本与符号对齐 */
+                    mso-special-format: bullet;
+                    margin-left: 0;
+                    padding-left: 10pt;
+                    text-indent: -10pt;
+                    mso-style-name: "Normal";
+                    mso-style-priority: 99;
+                    mso-style-unhide: no;
+                    mso-style-qformat: yes;
+                    mso-style-parent: "";
+			      			}
+			      			p{
+			      				margin:0;
+			      				padding:0
+			      			}
+                </style>
+            </head>
+            <body>
+            ${_html}
+            </body>
+            </html>`;
+          // console.log("生成blob",task)
+          // debugger
+          let blob = htmlDocx.asBlob(content);
+          const file = new File([blob], `${bmData.courseName}课堂观察报告.docx`, {
+            type: ".docx",
+            lastModified: new Date().getTime()
+          });
+          resolve(file)
+        } catch (error) {
+          console.log("error",error,task)
+          resolve(error)
+        }
+      })
+    },
+    getFileLastUpdateTime(file){
+      console.log(file)
+      if(!file)return new Date().toLocaleDateString().replaceAll("/","-") + ' ' + new Date().toLocaleTimeString();
+
+      const lastModifiedTimestamp  = file.lastModified;
+      console.log("lastModifiedTimestamp",lastModifiedTimestamp)
+      if(!lastModifiedTimestamp)return new Date().toLocaleDateString().replaceAll("/","-") + ' ' + new Date().toLocaleTimeString();
+      else return new Date(lastModifiedTimestamp).toLocaleDateString().replaceAll("/","-") + ' ' + new Date().toLocaleTimeString();
+    },
+    //获取词云图
+    getWordCloudMapMixin(data){
+      return new Promise((resolve)=>{
+        const _msg = `NOTICE
+        Language: Please use the same language as the user requirement, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
+        ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
+        Instruction: Based on the context, follow "Format example", write content.
+
+        ## 任务
+
+        请基于以下课堂实录文本(大约5000字),提炼出20-30个关键词,用于绘制词云图。请给出相应的关键词,关键词出现的频次,词云图上的大小。请确保输出的关键字准确反映课堂实录的主要内容和主题。
+
+        ## 要求
+
+        1. **提取关键词**:从提供的课堂实录文本中提取出20-30个最具代表性的关键字。关键词应该涵盖课堂实录中的主要概念、重要术语和核心主题。尽量选择多样化的关键词,避免过于集中在某一个主题或概念上。
+        2. **词频统计**:计算每个关键字在文本中出现的频率。
+        3. **词汇大小**:根据词频数量,确定每个关键字在词云图中的大小。词频越高,词汇大小数值越大,数值范围1-100。
+        4. **输出格式**:输出结果应包含每个关键字、对应的词频数量以及词汇大小数值。
+
+        ## 输出格式
+
+        ### 输出格式
+
+        [
+        	{"value":1,"name":"氯化钠","textStyle":{"color":"#ee7959"}},
+        	{"value":2,"name":"溶液","textStyle":{"color":"#db9b34"}},
+        	{"value":1,"name":"实验","textStyle":{"color":"#9d9d82"}},
+        	{"value":3,"name":"质量分数","textStyle":{"color":"#ea5514"}},
+        	{"value":1,"name":"溶质","textStyle":{"color":"#c8161d"}},
+        	{"value":2,"name":"氢氧化钠","textStyle":{"color":"#e60012"}},
+        	{"value":1,"name":"溶解度","textStyle":{"color":"#1e2732"}},
+        	{"value":4,"name":"饱和溶液","textStyle":{"color":"#e3adb9"}}
+        ]
+
+        请仅仅输出表头,输出关键词和相应的内容,无需其它任何说明文字。
+        冒号、逗号等符号均使用英文字符。
+
+        ### 输出示例
+
+        [
+        	{"value":1,"name":"氯化钠","textStyle":{"color":"#ee7959"}},
+        	{"value":2,"name":"溶液","textStyle":{"color":"#db9b34"}},
+        	{"value":1,"name":"实验","textStyle":{"color":"#9d9d82"}},
+        	{"value":3,"name":"质量分数","textStyle":{"color":"#ea5514"}},
+        	{"value":1,"name":"溶质","textStyle":{"color":"#c8161d"}},
+        	{"value":2,"name":"氢氧化钠","textStyle":{"color":"#e60012"}},
+        	{"value":1,"name":"溶解度","textStyle":{"color":"#1e2732"}},
+        	{"value":4,"name":"饱和溶液","textStyle":{"color":"#e3adb9"}}
+        ]
+
+        ## 课堂实录
+        ${data}
+        `;
+
+        const _uuid = uuidv4();
+				let params = {
+					temperature: 0,
+					max_tokens: 4096,
+					top_p: 1,
+					frequency_penalty: 0,
+					presence_penalty: 0,
+					messages: [{ role: "user", content: _msg }],
+					uid: _uuid,
+					mind_map_question: "",
+					stream: false,
+					model: "gpt-4o-2024-11-20",
+				};
+
+        this.ajax
+					.post("https://gpt4.cocorobo.cn/chat", params)
+					.then((res) => {
+						let _data = res.data.FunctionResponse.choices[0];
+						let _jsonData = _data.message.content;
+						_jsonData = _jsonData.replaceAll("```json", "").replaceAll("```", "");
+						let _result = JSON.parse(_jsonData);
+            resolve({
+              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: '90%',
+                  height: '90%',
+                  rotationRange: [-90, 90],
+                  rotationStep: 45,
+                  data: _result,
+                },
+              ],
+            })
+          })
+					.catch((e) => {
+						console.log(e);
+						resolve(0)
+
+					})
+
+      })
+    }
+  }
+};

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

+ 2 - 2
src/components/pages/sz/dataBoardArea/dataCenter/chartList/courseRank/index.vue

@@ -12,9 +12,9 @@
 					<template slot-scope="scope">
 						<div>
 							<!-- {{ Math.abs(scope.row.compare) }} -->
-							<div v-if="scope.row.compare>0" class="el-icon-top compareDown">{{ Math.abs(scope.row.compare) }}</div>
+							<div v-if="scope.row.compare>0" class="el-icon-bottom compareDown">{{ Math.abs(scope.row.compare) }}</div>
 							<div v-if="scope.row.compare==0">-</div>
-							<div v-if="scope.row.compare<0" class="el-icon-bottom compareUp">{{ Math.abs(scope.row.compare) }}</div>
+							<div v-if="scope.row.compare<0" class="el-icon-top compareUp">{{ Math.abs(scope.row.compare) }}</div>
 						</div>
 					</template>
         </el-table-column>

+ 2 - 2
src/components/pages/sz/dataBoardCity/dataCenter/chartList/courseRank/index.vue

@@ -12,9 +12,9 @@
 					<template slot-scope="scope">
 						<div>
 							<!-- {{ Math.abs(scope.row.compare) }} -->
-							<div v-if="scope.row.compare>0" class="el-icon-top compareDown">{{ Math.abs(scope.row.compare) }}</div>
+							<div v-if="scope.row.compare>0" class="el-icon-bottom compareDown">{{ Math.abs(scope.row.compare) }}</div>
 							<div v-if="scope.row.compare==0">-</div>
-							<div v-if="scope.row.compare<0" class="el-icon-bottom compareUp">{{ Math.abs(scope.row.compare) }}</div>
+							<div v-if="scope.row.compare<0" class="el-icon-top compareUp">{{ Math.abs(scope.row.compare) }}</div>
 						</div>
 					</template>
         </el-table-column>

+ 654 - 11
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"
@@ -214,6 +214,141 @@
                 >
                   AI分析
                 </div>
+
+
+
+                 <el-popover
+                 v-if="stype == 2"
+                  placement="top-start"
+                  width="320"
+                  ref="popover1"
+                  :visible-arrow="false"
+                  trigger="click"
+                  
+                  >
+                    <div class="openCeBox" v-loading="aiscoLoading">
+                        <div style="display: flex;justify-content: space-between;align-items: center;">
+                          <div class="TopTit">智能分析列</div>
+                          <el-popover
+                            placement="bottom"
+                            @click.native.stop
+                            :visible-arrow="false"
+                            width="300"
+                            ref="popover2"
+                            v-model="opQvisible"
+                            trigger="click">
+                            <div> 
+                              <div style="display: flex;align-items: center;margin-bottom: 10px;">
+                                <img src="../../../../assets/delicon.svg" alt="">是否确认清空
+                              </div>
+                              <div style="padding: 0 20px;box-sizing: border-box;">清空会删除所有配置信息,并同步删除已经生成完成的数据列。请谨慎操作!</div>
+                              <div style="display: flex;justify-content: flex-end;margin-top: 10px;">
+                                  <el-button class="BtnOPC" @click="opQvisible = false"> 取消</el-button>
+                                  <el-button class="BtnOPC" @click="cleancueWord" style="background: #1456F0;" type="primary">确定</el-button>
+                              </div>
+                            </div>
+                            <div class="Topkong" slot="reference">
+                              <img src="../../../../assets/clear.svg" alt="">
+                              清空
+                            </div> 
+                          </el-popover>
+
+                        </div>
+                        <div style="font-size: 12px;color: #6E6B6B;">标题</div>
+                        <div>
+                          <el-input v-model="cueWord.openCeTit" placeholder="输入列标题"></el-input>
+                        </div>
+                        <div style="font-size: 12px;color: #6E6B6B;">配置</div>
+                        <div style="background: #F5F6F7;padding: 10px;box-sizing: border-box;border-radius: 5px;">
+                          <div style="display: flex;justify-content: space-between;align-items: center;margin-bottom: 5px;">
+                            <div style="font-size: 12px;color: #6E6B6B;">输入指令*</div>
+                              
+                              <div style="position: relative;font-size: 12px;color: #3992EA;" >
+                                <div style="cursor: pointer;" @click="quoteShow">
+                                    <i class="el-icon-circle-plus-outline"></i>
+                                    引用字段
+                                </div>
+                                
+                              
+                                <div class="quoteBlock" v-if="quoteV">
+                                  <el-input  v-model="opcInp" placeholder="请输入内容">
+                                    <i slot="prefix" class="el-input__icon el-icon-search"></i>
+                                  </el-input>
+                                  <div class="quoteBlockCon">
+                                    <div v-if="!opcInp.trim() ? true : '序号'.includes(opcInp)" :class="[cueWord.fieldList.indexOf('序号') != -1 ? 'selCss' :'']" 
+                                    @click="addfield('序号')">序号</div>
+
+                                    <div v-if="!opcInp.trim() ? true :'提交人'.includes(opcInp)" :class="[cueWord.fieldList.indexOf('提交人') != -1 ? 'selCss' :'']" 
+                                    @click="addfield('提交人')">提交人</div>
+
+                                    <template  v-for="(i,index) in teaType">
+                                      <div v-if="!opcInp.trim() ? true : i.name.includes(opcInp)" :class="[cueWord.fieldList.indexOf(i.name) != -1 ? 'selCss' :'']" 
+                                       @click="addfield(i.name)" :key="index+'a'">
+                                        {{ i.name }}
+                                      </div>
+                                    </template>
+                                    
+
+                                    <div v-if="!opcInp.trim() ? true : '提交时间'.includes(opcInp)" :class="[cueWord.fieldList.indexOf('提交时间') != -1 ? 'selCss' :'']" 
+                                    @click="addfield('提交时间')">提交时间</div>
+
+                                    <template v-for="(i,index) in fie(chapters)">
+                                      <div :class="[cueWord.fieldList.indexOf(i.json.title) != -1 ? 'selCss' :'']" 
+                                        v-if="!opcInp.trim() ? true :i.json.title.includes(opcInp)" @click="addfield(i.json.title)" 
+                                        :key="index+'b'">
+                                        {{ i.json.title }}
+                                      </div>
+                                    </template>
+                                    
+                                  </div>
+                                 
+                                </div>
+                                <div class="maskCss" v-if="quoteV" @click.stop="quoteV = false,opcInp=''"></div>
+
+                              </div>
+
+                          </div>
+                          <div style="display: flex;gap: 8px;margin-bottom: 5px;flex-wrap: wrap;">
+                            <span class="SPCE" v-for="(i,index) in cueWord.fieldList" :key="index+'c'">
+                              {{ i }} <i @click="addfield(i)" class="el-icon-close" style="cursor: pointer;"></i>
+                            </span>
+                          </div>
+                          <div>
+                            <el-input
+                              resize="none"
+                              type="textarea"
+                              :rows="8"
+                              placeholder="请输入内容"
+                              v-model="cueWord.openCeCon">
+                            </el-input>
+                          </div>
+                        </div>
+                        <!-- <div style="display: flex;justify-content: flex-start;align-items: center;gap: 5px;">
+                          <el-switch
+                            v-model="openCeVal"
+                            >
+                          </el-switch>
+                          <span>自动更新
+                             <el-tooltip class="item" effect="dark" content="开启后,当前字段将跟随配置内容的变化同步更新" placement="top">
+                                <i class="el-icon-warning-outline"></i>
+                              </el-tooltip>
+                            </span>
+                        </div> -->
+                        <div style="margin-top: 10px;display: flex;justify-content: flex-end;">
+                          <el-button @click="opQvisible0 = false">取消</el-button>
+                          <el-button style="background: #1456F0;" @click="markSco()" type="primary">确定</el-button>
+                        </div>
+                    </div>
+
+
+                    <div
+                      slot="reference"
+                      class="btnA"
+                    >
+                      智能分析列
+                    </div>
+                </el-popover>
+
                 <!--v-if="this.worksArray.length"-->
               </div>
             </div>
@@ -1169,7 +1304,7 @@
                 width="170px"
                 align="left"
               >
-              <template slot="header" slot-scope="scope">
+              <template slot="header">
                   <div
                   style="display: flex; align-items: center"
                   >
@@ -1228,7 +1363,7 @@
                 "
               >
                 <!--  item.type==8?getTimeFilterText(item,index): item.type==8?(value,row)=>{return filterTime(value,row,index)}:  item.type == 8 ||-->
-                <template slot="header" slot-scope="scope">
+                <template slot="header">
                   <div
                     v-if="item.type == 8"
                     style="display: flex; align-items: center"
@@ -1369,6 +1504,21 @@
                   </div>
                 </template>
               </el-table-column>
+              <el-table-column
+                label="dee"
+                width="150px"
+                fixed="right"
+                v-if="scoList.length"
+              >
+              <template #header>
+                <div>
+                  <span>{{ cueWordCopy.openCeTit }} <span class="proBar" v-if="aiscoLoading">AI生成中{{ LoadingProgress }}%</span></span>
+                </div>
+              </template>
+                <template slot-scope="scope">
+                  <span>{{ userSco(scope.row.userid) }}</span>
+                </template>
+              </el-table-column>
               <el-table-column
                 label="操作"
                 width="200px"
@@ -1379,8 +1529,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 +1540,7 @@
                     @click="deleteTest(scope.row.id)"
                     type="primary"
                     size="small"
+                     v-if="(examineData && examineData.type == 1) || !examineData"
                     >删除</el-button
                   >
                 </template>
@@ -1895,6 +2048,26 @@ export default {
   },
   data() {
     return {
+      aiscoId:'',
+      aiscoLoading:true,
+      cueWord:{
+        openCeCon:'', //指令
+        openCeTit:'', //标题
+        fieldList:[]
+      },
+      cueWordCopy:{
+        openCeCon:'', //指令
+        openCeTit:'', //标题
+        fieldList:[]
+      },
+      scoList:[],
+      quoteV:false, 
+      openCeVal:false,
+      LoadingProgress:0,
+      opQvisible:false,
+      opQvisible2:false,
+      opcInp:'',
+      languageSetting: 0,
       userid: this.$route.query.userid,
       oid: this.$route.query.oid,
       org: this.$route.query.org,
@@ -1904,6 +2077,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,10 +2134,29 @@ export default {
       // fileLoadNum: [],
       // infoprogress: [],
       teaType: [],
+      examineData:null,
     };
   },
   watch: {},
   computed: {
+     // 去除不能评分的指令
+    fie(){
+      return function (val) {
+        console.log('fie',val);
+        
+        const con = val.filter(e => [12,11,6,14].indexOf(e.type) == -1) 
+        return con
+      }
+    },
+    // ai评分分数
+    userSco(){
+      return function (val) {
+        const con = this.scoList.find(u => u.uid === val);
+        // console.log('con',con);
+        
+        return con ? con.sco : '';
+      }
+    },
     TeachNameCpt: function () {
       // `this` 指向 vm 实例
       return this.TeachingOptions.filter((i) => {
@@ -2127,6 +2321,337 @@ export default {
     }
   },
   methods: {
+// 清除分数
+    cleancueWord(){
+      if (!this.aiscoId) return
+      let params = [{
+        tid:this.cid
+      }]
+      console.log('params',params);
+       this.ajax
+        .post(this.$store.state.api + "deltestAisco", params)
+        .then((res) => {
+          console.log(res);
+           this.$message({
+            message: "删除成功",
+            type: "success"
+          });
+          this.getAiSco()
+        })
+        .catch((err) => {
+          this.$message({
+            message: "删除失败",
+            type: "error"
+          });
+          console.error(err);
+        });
+        this.opQvisible = false
+    },
+ // 点击开始评分
+    async markSco(){
+      if (!this.cueWord.openCeTit.trim()) return this.$message.error('请输入标题')
+      if (!this.cueWord.openCeCon.trim()) return this.$message.error('请输入指令')
+      if (this.cueWord.fieldList.length == 0) return this.$message.error('请选择引用字段')
+        this.aiscoLoading = true
+
+      let forAllList = [];
+      // this.scoList = []
+
+      for (let index = 0; index < this.tableWorksArray.length; index++) {
+          let mkAwit = this.loopSco(this.tableWorksArray[index],index)
+          forAllList.push(mkAwit);
+      }
+
+      Promise.all(forAllList)
+        .then(values => {
+          console.log("values", values);
+          
+          this.uploadSCo()
+
+          setTimeout(() => {
+            this.LoadingProgress = 0
+          }, 1000);
+        })
+        .catch(reason => {
+          this.aiscoLoading = false
+
+          this.$message({
+            message: "评价失败",
+            type: "error"
+          });
+          console.error("reasonreasonreasonreasonreason", reason);
+          // 输出“失败原因”
+        });
+    },
+    // ai评分循环
+    async loopSco(val){
+      console.log('6666',val);
+      
+      let userWork =[]
+      let _fileid = ''
+        for (let index = 0; index < val.array.length; index++) {
+          if (val.array[index].type == 3) {
+            userWork.push( {
+              // type:'文本',
+              tit: val.array[index].json.title,
+              con: val.array[index].json.answer2
+            })
+          }
+          if (val.array[index].type == 5) {
+            
+            if (val.array[index].json.file && val.array[index].json.file[0].url && this.cueWord.fieldList.indexOf('附件') != -1) {
+              _fileid = await this.createFileid(val.array[index].json.file[0].url)
+            }
+
+            userWork.push( {
+              // type:"附件",
+              detail:val.array[index].json.detail,
+              tit: val.array[index].json.title,
+              con: _fileid
+            })
+          }
+          if (val.array[index].type == 7) {
+            userWork.push( {
+              // type:"评分",
+              rule:`${val.array[index].json.small}分-${val.array[index].json.big}分`,
+              detail:val.array[index].json.detail,
+              tit: val.array[index].json.title,
+              con: val.array[index].json.answer2
+            })
+          }
+          if (val.array[index].type == 1) {
+            let kk = []
+            val.array[index].json.answer2.forEach((e)=>{
+              kk.push(val.array[index].json.array[e].option)
+            })
+            userWork.push( {
+              tit: val.array[index].json.title,
+              con: kk.join(','),
+              type: val.array[index].json.type == 1?"单选题": "多选题",
+            })
+          }
+        }
+      return new Promise((resolve, reject) => {
+        const result = userWork.filter(item => this.cueWord.fieldList.includes(item.tit));
+
+        let messages = `NOTICE
+                Role: 你是一个资深的教师,你要根据评分标准,对用户回答进行评分
+                  Language: ${this.getLang()}
+
+                  #目标
+                  作业内容中,tit是题目,con是用户回答。
+
+                  #评分资料#
+                  用户信息:
+                    ${this.cueWord.fieldList.includes('提交人') ? '提交人:' +val.name :''}
+                    ${this.cueWord.fieldList.includes('学段') ? '学段:' +this.getTType(this.teaType[0].id,val.cclassid) :''}
+                    ${this.cueWord.fieldList.includes('年级') ? '年级:' +this.getTType(this.teaType[1].id,val.cclassid) :''}
+                    ${this.cueWord.fieldList.includes('学科') ? '学科:' +this.getTType(this.teaType[2].id,val.cclassid) :''}
+                    ${this.cueWord.fieldList.includes('职务') ? '职务:' +this.getTType(this.teaType[3].id,val.cclassid) :''}
+                    ${this.cueWord.fieldList.includes('教龄') ? '教龄:' +this.getTType(this.teaType[4].id,val.cclassid) :''}
+                    ${this.cueWord.fieldList.includes('作业提交时间') ? '作业提交时间:' +val.time :''}
+
+                  评价标准:${this.cueWord.openCeCon}
+                  作业内容 :${_fileid ? "知识库中上传的文件内容与"+JSON.stringify(result)  : JSON.stringify(result)}
+                
+              
+                  #输出要求#
+                  未找到评价内容或相关信息的评为0分。
+                  直接输出分数!不要输入任何其他无关内容!
+
+                  #输出格式#
+                  0
+                  `;
+              
+                  // console.log('messages',messages);
+                  
+          let _this = this;
+          let params = {
+          assistant_id: "6063369f-289a-11ef-8bf4-12e77c4cb76b",
+          message: [
+            {
+              type: "text",
+              text: messages.replaceAll("\n", " ").replaceAll("*", "")
+            }
+          ],
+          session_name: uuidv4(),
+          userId: this.userid,
+          file_ids: _fileid ? [_fileid] : [],
+          isImage: "",
+          model: "gpt-4o-2024-11-20"
+          };
+          this.ajax
+            .post("https://gpt4.cocorobo.cn/ai_agent_park_chat", params)
+            .then(response => {
+                 let data = response.data.FunctionResponse;
+                  console.log("data", data);
+
+              try {
+                  //  const prompt = [
+                  //     {
+                  //       role: 'user',
+                  //       content: `输出一个json格式的格式如下:{uid:${ val.userid },sco: ${data.message}}}`,
+                  //     },
+                  //   ];
+                  //   this.chat_no_stream(prompt, { type: 'json_object' }).then((res) => {
+                  //     // this.suggestList = JSON.parse(res).suggest;
+                  //     console.log(res);
+                  //   });
+                  let ind = this.scoList.findIndex(e=> e.uid == val.userid )
+                 console.log('ind',ind);
+
+                  if (ind != -1) {
+                    this.scoList.splice(ind,1,{uid: val.userid ,sco: data.message})
+                  }else{
+                    this.scoList.push({uid: val.userid ,sco: data.message})
+                  }
+
+              } catch (error) {
+                 let ind = this.scoList.findIndex(e=> e.uid == val.userid )
+                 console.log('ind',ind);
+                 
+                  if (ind != -1) {
+                    this.scoList.splice(ind,1,{uid: val.userid ,sco: ''})
+                  }else{
+                    this.scoList.push({uid: val.userid ,sco: ''})
+                  }
+                // this.scoList.push({uid: val.userid ,sco: data.message})
+                this.$message.error( val.name + '评分失败')
+              }
+              this.LoadingProgress = ((this.scoList.length / this.tableWorksArray.length) * 100).toFixed(0)
+
+              return resolve(1)
+            })
+            .catch(error => {
+              this.aiscoLoading = false
+              this.$message.error('评分失败')
+              reject(error)
+              console.log(error);
+            });
+        })
+      
+    },
+    // 获取评分
+    async getAiSco(){
+      return new Promise((resolve, reject) => {
+        this.aiscoLoading = true
+        let params = {
+          tid: this.cid,
+        };
+        this.ajax
+          .get(this.$store.state.api + "getTestAiSco", params)
+          .then((res) => {
+            if (res.data[0].length) {
+                this.aiscoId = res.data[0][0].id
+                this.cueWord = JSON.parse(res.data[0][0].cueWord)
+                this.cueWordCopy = JSON.parse(res.data[0][0].cueWord)
+                this.scoList = JSON.parse(res.data[0][0].json)
+            }else{
+              this.cueWord={
+                openCeCon:'', //指令
+                openCeTit:'', //标题
+                fieldList:[]
+              }
+              this.cueWordCopy={
+                openCeCon:'', //指令
+                openCeTit:'', //标题
+                fieldList:[]
+              }
+              this.scoList=[]
+            }
+            this.aiscoLoading = false
+             
+              console.log('8888888888',res.data[0]);
+              resolve(1)
+          })
+          .catch((err) => {
+            reject(err)
+            this.aiscoLoading = false
+
+            console.error(err);
+          });
+      })
+      
+    },
+    // 上传评分
+    uploadSCo(){
+      this.aiscoLoading = true
+
+      let params = [{
+        tid:this.cid,
+        uid:'6063369f-289a-11ef-8bf4-12e77c4cb76b',
+        json:JSON.stringify(this.scoList),
+        cw: JSON.stringify(this.cueWord)
+      }]
+      console.log('params',params);
+       this.ajax
+        .post(this.$store.state.api + "addtestAiSco", params)
+        .then((res) => {
+          console.log(res);
+           this.$message({
+            message: "评价成功",
+            type: "success"
+          });
+          this.getAiSco()
+        })
+        .catch((err) => {
+          this.aiscoLoading = false
+          this.$message({
+            message: "评价失败",
+            type: "error"
+          });
+          console.error(err);
+        });
+    },
+    // 获取文档id
+    createFileid(url) {
+      let _this = this;
+      return new Promise((resolve, reject) => {
+        try {
+          _this.ajax
+            .put("https://gpt4.cocorobo.cn/upload_file_knowledge", {
+              url: url
+            })
+            .then(res => {
+              let _data = res.data.FunctionResponse;
+              if (_data.result && _data.result.id) {
+                resolve(_data.result.id);
+              } else {
+                resolve(1);
+              }
+            })
+            .catch(function(error) {
+              resolve(1);
+            });
+        } catch (e) {
+          resolve(1);
+        }
+      });
+    },
+    getLang() {
+      let lang = "";
+      if (this.languageSetting == 0) {
+        lang = "Chinese.";
+      } else if (this.languageSetting == 1) {
+        lang = "Traditional Chinese.";
+      } else if (this.languageSetting == 2) {
+        lang = "English.";
+      }
+      return lang;
+    },
+    // 打开引用字段弹框
+    quoteShow(){
+      this.quoteV = !this.quoteV 
+    },
+    // 添加指令字段
+    addfield(val){
+      if (this.cueWord.fieldList.indexOf(val) != -1) {
+        const index = this.cueWord.fieldList.indexOf(val)
+        this.cueWord.fieldList.splice(index, 1)
+      }else{
+        this.cueWord.fieldList.push(val)
+      }
+    },
     openChat() {
       this.aiChatV = !this.aiChatV;
       this.$nextTick(() => {
@@ -2311,8 +2836,10 @@ export default {
       }
     },
 
-    getData() {
+    async getData() {
       this.isLoading = true;
+      await this.getAiSco()
+
       let params = [
         {
           cid: this.cid,
@@ -3852,6 +4379,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 +4413,10 @@ ${JSON.stringify(item.array)}
       } else {
         this.getData();
       }
+
+      if(this.examineId){
+        this.getExamineData();
+      }
       // this.getClass2()
       this.changeHeight();
     });
@@ -4043,8 +4591,8 @@ ${JSON.stringify(item.array)}
   color: #00000099;
 }
 
-.test_title .left .info .info_box span:nth-child(2) {
-}
+/* .test_title .left .info .info_box span:nth-child(2) {
+} */
 
 .test_title .right {
   display: flex;
@@ -4067,11 +4615,11 @@ ${JSON.stringify(item.array)}
   align-items: center;
 }
 
-.test_title .right .data_box span:nth-child(1) {
+/* .test_title .right .data_box span:nth-child(1) {
 }
 
 .test_title .right .data_box span:nth-child(2) {
-}
+} */
 
 .test_title .right .data_box span:nth-child(2) .big {
   font-size: 22px;
@@ -4100,8 +4648,8 @@ ${JSON.stringify(item.array)}
   background-size: 100% 100%;
 }
 
-.test_title .right .btn_box span:nth-child(2) {
-}
+/* .test_title .right .btn_box span:nth-child(2) {
+} */
 
 .search_nav {
   border-bottom: 1px solid #e7e7e7;
@@ -4866,4 +5414,99 @@ ${JSON.stringify(item.array)}
 .timePickerActive{
   color: #409EFF;
 }
+.openCeBox{
+  width: 320px;
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  font-size: 14px !important;
+  padding: 10px;
+  box-sizing: border-box;
+}
+.TopTit{
+  font-family: PingFang SC;
+  font-weight: 600;
+  font-size: 16px;
+  line-height: 24px;
+  color: #000;
+}
+.Topkong{
+  font-family: PingFang SC;
+  font-weight: 400;
+  font-size: 12px;
+  color: #000;
+  display: flex;
+  align-items: center;
+  gap: 5px;
+  cursor: pointer;
+}
+.BtnOPC{
+    width: 50px;height: 30px;font-size: 14px;display: flex;
+    justify-content: center;
+    align-items: center;
+    background: #E7E7E7;
+}
+.quoteBlock{
+  position: absolute;
+  right: 0;
+  width: 220px;
+  background: #fff;
+  border-radius: 10px;
+  color: #000;
+  z-index: 100001;
+  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* 浅灰色阴影 */
+  border-radius: 5px;overflow: hidden;
+}
+.quoteBlock >>> .el-input__inner{
+    border-radius: 0 !important;
+    border: none !important;
+    box-shadow: none !important;
+   resize: none !important;
+   border-bottom: 1px #DCDFE6 solid !important;
+}
+.quoteBlockCon{
+  padding: 10px 10px;box-sizing: border-box;
+  display: flex;
+  flex-direction: column;
+  gap: 8px;
+  font-size: 14px;
+  height: 200px;
+  overflow: auto;
+}
+
+.quoteBlockCon > div{
+  height: 25px;line-height: 25px;
+  cursor: pointer;
+  padding: 0 5px;
+  box-sizing: border-box;
+  border-radius: 5px;
+}
+.maskCss{
+  /* background: #000; */
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 10000;
+  opacity: .5;
+}
+.SPCE{
+  font-size: 12px;
+  background: #fff;
+  padding: 2px 5px;box-sizing: border-box;
+  border-radius: 3px;
+}
+.selCss{
+  background: #e7e7e7;
+  color:#0061ff;
+}
+.proBar{
+    background: #dae0f0;
+    font-size: 14px;
+    font-weight: normal;
+    color: #6f93ea;
+    padding: 4px;
+    border-radius: 3px;
+}
 </style>

+ 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;
     },

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff