Forráskód Böngészése

Merge remote-tracking branch 'origin/beta' into HK

qgt 2 hónapja
szülő
commit
b9ef9886f9
35 módosított fájl, 3642 hozzáadás és 141 törlés
  1. 4 0
      dist/index.html
  2. 0 0
      dist/static/css/app.4e6afdd2b9dc8130c5f9f2992c271a15.css.map
  3. 0 0
      dist/static/css/app.8f19f756a441c1214dc059ca3531cf96.css
  4. 1 0
      dist/static/css/app.8f19f756a441c1214dc059ca3531cf96.css.map
  5. 0 0
      dist/static/js/0.4f3b05586c3acc102a54.js.map
  6. 0 0
      dist/static/js/0.df8814bab917ab2583e0.js
  7. 0 0
      dist/static/js/0.df8814bab917ab2583e0.js.map
  8. 1 0
      dist/static/js/app.3cc50ae1e5acbf2187d9.js
  9. 0 0
      dist/static/js/app.3cc50ae1e5acbf2187d9.js.map
  10. 1 0
      dist/static/js/app.8210568261f9561a212b.js
  11. 1 0
      dist/static/js/app.8210568261f9561a212b.js.map
  12. 6 1
      dist/static/js/manifest.3fb62e91643ccf3ef432.js
  13. 0 0
      dist/static/js/manifest.3fb62e91643ccf3ef432.js.map
  14. 7 0
      dist/static/js/manifest.9811ebe9d5c4458a1b2a.js
  15. 1 0
      dist/static/js/manifest.9811ebe9d5c4458a1b2a.js.map
  16. 5 0
      src/assets/icon/classroomObservation/audio_file.svg
  17. 1 0
      src/assets/icon/classroomObservation/batch_icon.svg
  18. 3 0
      src/assets/icon/classroomObservation/close.svg
  19. 7 0
      src/assets/icon/classroomObservation/file_icon.svg
  20. 20 0
      src/assets/icon/classroomObservation/file_processing.svg
  21. 4 0
      src/assets/icon/classroomObservation/table_edit.svg
  22. 7 0
      src/assets/icon/classroomObservation/textFile_icon.svg
  23. 5 0
      src/assets/icon/classroomObservation/videoFile_icon.svg
  24. 111 99
      src/common/axios.config.js
  25. 414 33
      src/components/pages/classroomObservation/components/chatArea.vue
  26. 5 2
      src/components/pages/classroomObservation/components/previewVideoDialog.vue
  27. 768 0
      src/components/pages/classroomObservation/dialog/batchCreationClassDialog.vue
  28. 956 0
      src/components/pages/classroomObservation/dialog/editBaseMessageDialog.vue
  29. 547 0
      src/components/pages/classroomObservation/dialog/uploadFileToCreateClassDialog.vue
  30. 20 2
      src/components/pages/classroomObservation/index.vue
  31. 384 0
      src/components/pages/classroomObservation/newComponents/batchClassCard.vue
  32. 327 0
      src/components/pages/classroomObservation/newComponents/uploadFile.vue
  33. 1 0
      src/components/pages/test/check/docxTemplateDialog.vue
  34. 18 2
      src/components/pages/test/examine/conpoments/targetPage.vue
  35. 17 2
      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.4e6afdd2b9dc8130c5f9f2992c271a15.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.3fb62e91643ccf3ef432.js></script><script type=text/javascript src=./static/js/vendor.bb486323f0fa002ba2e7.js></script><script type=text/javascript src=./static/js/app.3cc50ae1e5acbf2187d9.js></script></body></html><script>function stopSafari() {
+=======
+    }</style><link href=./static/css/app.8f19f756a441c1214dc059ca3531cf96.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.9811ebe9d5c4458a1b2a.js></script><script type=text/javascript src=./static/js/vendor.bb486323f0fa002ba2e7.js></script><script type=text/javascript src=./static/js/app.8210568261f9561a212b.js></script></body></html><script>function stopSafari() {
+>>>>>>> origin/beta
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
dist/static/css/app.4e6afdd2b9dc8130c5f9f2992c271a15.css.map


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
dist/static/css/app.8f19f756a441c1214dc059ca3531cf96.css


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
dist/static/css/app.8f19f756a441c1214dc059ca3531cf96.css.map


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
dist/static/js/0.4f3b05586c3acc102a54.js.map


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
dist/static/js/0.df8814bab917ab2583e0.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
dist/static/js/0.df8814bab917ab2583e0.js.map


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
dist/static/js/app.3cc50ae1e5acbf2187d9.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
dist/static/js/app.3cc50ae1e5acbf2187d9.js.map


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
dist/static/js/app.8210568261f9561a212b.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
dist/static/js/app.8210568261f9561a212b.js.map


+ 6 - 1
dist/static/js/manifest.3fb62e91643ccf3ef432.js

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

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 0 - 0
dist/static/js/manifest.3fb62e91643ccf3ef432.js.map


+ 7 - 0
dist/static/js/manifest.9811ebe9d5c4458a1b2a.js

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

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 0
dist/static/js/manifest.9811ebe9d5c4458a1b2a.js.map


+ 5 - 0
src/assets/icon/classroomObservation/audio_file.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5.5 6.4C5.5 5.56 5.5 5.14 5.663 4.819C5.80685 4.53651 6.03651 4.30685 6.319 4.163C6.639 4 7.059 4 7.9 4H12.506C12.873 4 13.056 4 13.229 4.041C13.3837 4.07833 13.528 4.13833 13.662 4.221C13.814 4.314 13.943 4.444 14.202 4.703L17.797 8.297C18.057 8.557 18.187 8.687 18.279 8.837C18.3617 8.973 18.4217 9.11767 18.459 9.271C18.5 9.444 18.5 9.627 18.5 9.994V18.6C18.5 19.44 18.5 19.86 18.337 20.181C18.1931 20.4635 17.9635 20.6931 17.681 20.837C17.361 21 16.941 21 16.1 21H7.9C7.06 21 6.64 21 6.319 20.837C6.03651 20.6931 5.80685 20.4635 5.663 20.181C5.5 19.861 5.5 19.441 5.5 18.6V6.4Z" stroke="black"/>
+<path d="M12.5 4V7.6C12.5 8.44 12.5 8.86 12.664 9.181C12.8076 9.46334 13.0369 9.69298 13.319 9.837C13.639 10 14.059 10 14.9 10H18.5" stroke="black"/>
+<path d="M11.7619 15.1825L11.3175 11.5H15V12.8334H12.2381L12.6825 16.7225C12.6824 17.1139 12.5486 17.4943 12.3018 17.8047C12.055 18.1151 11.709 18.3383 11.3175 18.4395C10.926 18.5407 10.5108 18.5144 10.1364 18.3646C9.76197 18.2148 9.44919 17.9498 9.24658 17.6109C9.04397 17.272 8.96284 16.8779 9.01577 16.4899C9.06871 16.1019 9.25276 15.7416 9.53937 15.4649C9.82597 15.1882 10.1991 15.0105 10.601 14.9595C11.0028 14.9084 11.4109 14.9868 11.7619 15.1825Z" fill="black"/>
+</svg>

+ 1 - 0
src/assets/icon/classroomObservation/batch_icon.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747704120613" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6001" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M296.533333 712.832a42.666667 42.666667 0 0 1 0 60.330667l-90.538666 90.538666a42.666667 42.666667 0 0 1-60.330667 0l-30.165333-30.165333a42.666667 42.666667 0 0 1 60.330666-60.373333l60.373334-60.330667a42.666667 42.666667 0 0 1 60.330666 0zM896 768a42.666667 42.666667 0 0 1 0 85.333333H426.666667a42.666667 42.666667 0 0 1 0-85.333333h469.333333zM296.533333 414.165333a42.666667 42.666667 0 0 1 0 60.330667l-90.538666 90.538667a42.666667 42.666667 0 0 1-60.330667 0l-30.165333-30.165334a42.666667 42.666667 0 0 1 60.330666-60.373333l60.373334-60.330667a42.666667 42.666667 0 0 1 60.330666 0zM896 469.333333a42.666667 42.666667 0 0 1 0 85.333334H426.666667a42.666667 42.666667 0 0 1 0-85.333334h469.333333zM296.533333 115.498667a42.666667 42.666667 0 0 1 0 60.330666L205.994667 266.368a42.666667 42.666667 0 0 1-60.330667 0l-30.165333-30.165333A42.666667 42.666667 0 0 1 175.829333 175.786667l60.373334-60.330667a42.666667 42.666667 0 0 1 60.330666 0zM896 170.666667a42.666667 42.666667 0 0 1 0 85.333333H426.666667a42.666667 42.666667 0 1 1 0-85.333333h469.333333z" fill="#ffffff" p-id="6002"></path></svg>

+ 3 - 0
src/assets/icon/classroomObservation/close.svg

@@ -0,0 +1,3 @@
+<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4.99147 3.81283L10.0001 8.82121L15.0088 3.81283C15.1715 3.65012 15.4354 3.65012 15.5981 3.81283L16.1873 4.40209C16.35 4.56481 16.35 4.82863 16.1873 4.99135L11.1788 9.99996L16.1873 15.0087C16.35 15.1714 16.35 15.4352 16.1873 15.5979L15.5981 16.1872C15.4354 16.3499 15.1715 16.3499 15.0088 16.1872L10.0001 11.1787L4.99147 16.1872C4.82875 16.3499 4.56493 16.3499 4.40221 16.1872L3.81296 15.5979C3.65024 15.4352 3.65024 15.1714 3.81296 15.0087L8.82133 9.99996L3.81296 4.99135C3.65024 4.82863 3.65024 4.56481 3.81296 4.40209L4.40221 3.81283C4.56493 3.65012 4.82875 3.65012 4.99147 3.81283Z" fill="black" fill-opacity="0.9"/>
+</svg>

+ 7 - 0
src/assets/icon/classroomObservation/file_icon.svg

@@ -0,0 +1,7 @@
+<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8.25 8.85C8.25 7.59 8.25 6.96 8.4945 6.4785C8.71028 6.05476 9.05476 5.71028 9.4785 5.4945C9.9585 5.25 10.5885 5.25 11.85 5.25H18.759C19.3095 5.25 19.584 5.25 19.8435 5.3115C20.0755 5.3675 20.292 5.4575 20.493 5.5815C20.721 5.721 20.9145 5.916 21.303 6.3045L26.6955 11.6955C27.0855 12.0855 27.2805 12.2805 27.4185 12.5055C27.5425 12.7095 27.6325 12.9265 27.6885 13.1565C27.75 13.416 27.75 13.6905 27.75 14.241V27.15C27.75 28.41 27.75 29.04 27.5055 29.5215C27.2897 29.9452 26.9452 30.2897 26.5215 30.5055C26.0415 30.75 25.4115 30.75 24.15 30.75H11.85C10.59 30.75 9.96 30.75 9.4785 30.5055C9.05476 30.2897 8.71028 29.9452 8.4945 29.5215C8.25 29.0415 8.25 28.4115 8.25 27.15V8.85Z" stroke="black" stroke-width="1.5"/>
+<path d="M18.75 5.25V10.65C18.75 11.91 18.75 12.54 18.996 13.0215C19.2114 13.445 19.5553 13.7895 19.9785 14.0055C20.4585 14.25 21.0885 14.25 22.35 14.25H27.75" stroke="black" stroke-width="1.5"/>
+<rect x="12" y="16.5" width="12" height="1.5" rx="0.75" fill="black"/>
+<rect x="12" y="21" width="12" height="1.5" rx="0.75" fill="black"/>
+<rect x="12" y="25.5" width="12" height="1.5" rx="0.75" fill="black"/>
+</svg>

+ 20 - 0
src/assets/icon/classroomObservation/file_processing.svg

@@ -0,0 +1,20 @@
+<svg width="142" height="142" viewBox="0 0 142 142" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_2392_7047)">
+<mask id="mask0_2392_7047" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="142" height="142">
+<path d="M142 0H0V142H142V0Z" fill="white"/>
+</mask>
+<g mask="url(#mask0_2392_7047)">
+<path d="M71.0002 88.4372C87.5049 88.4372 100.905 90.6133 100.905 93.2936C100.905 95.9738 87.5049 98.15 71.0002 98.15C54.4955 98.15 41.095 95.9738 41.095 93.2936C41.095 90.6133 54.4955 88.4372 71.0002 88.4372Z" fill="#BFD9FF"/>
+<path d="M46.5762 43.31H60.837L65.0768 50.836H95.1402V74.55H46.5762V43.31Z" fill="#126DFF"/>
+<path d="M89.3181 30.6104V68.9504H52.6821V30.6104H89.3181Z" fill="#87B5FF"/>
+<path d="M91.3061 44.8105V80.0265H50.6941V44.8105H91.3061Z" fill="#C2DAFF"/>
+<path d="M93.1521 55.2402V86.7642H49.1321V55.2402H93.1521Z" fill="white"/>
+<path d="M46.2034 67.5648L95.7969 67.5648L95.2821 94.4307H46.7181L46.2034 67.5648Z" fill="#5094FF"/>
+</g>
+</g>
+<defs>
+<clipPath id="clip0_2392_7047">
+<rect width="142" height="142" fill="white"/>
+</clipPath>
+</defs>
+</svg>

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

@@ -0,0 +1,4 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.66663 4.66663H3.99996C3.64634 4.66663 3.3072 4.8071 3.05715 5.05715C2.8071 5.3072 2.66663 5.64634 2.66663 5.99996V12C2.66663 12.3536 2.8071 12.6927 3.05715 12.9428C3.3072 13.1928 3.64634 13.3333 3.99996 13.3333H9.99996C10.3536 13.3333 10.6927 13.1928 10.9428 12.9428C11.1928 12.6927 11.3333 12.3536 11.3333 12V11.3333" stroke="#969BA3" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10.6667 3.33328L12.6667 5.33328M13.59 4.38995C13.8526 4.12739 14.0001 3.77127 14.0001 3.39995C14.0001 3.02863 13.8526 2.67251 13.59 2.40995C13.3274 2.14738 12.9713 1.99988 12.6 1.99988C12.2287 1.99988 11.8726 2.14738 11.61 2.40995L6 7.99995V9.99995H8L13.59 4.38995Z" stroke="#969BA3" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>

+ 7 - 0
src/assets/icon/classroomObservation/textFile_icon.svg

@@ -0,0 +1,7 @@
+<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5.5 6.4C5.5 5.56 5.5 5.14 5.663 4.819C5.80685 4.53651 6.03651 4.30685 6.319 4.163C6.639 4 7.059 4 7.9 4H12.506C12.873 4 13.056 4 13.229 4.041C13.3837 4.07833 13.528 4.13833 13.662 4.221C13.814 4.314 13.943 4.444 14.202 4.703L17.797 8.297C18.057 8.557 18.187 8.687 18.279 8.837C18.3617 8.973 18.4217 9.11767 18.459 9.271C18.5 9.444 18.5 9.627 18.5 9.994V18.6C18.5 19.44 18.5 19.86 18.337 20.181C18.1931 20.4635 17.9635 20.6931 17.681 20.837C17.361 21 16.941 21 16.1 21H7.9C7.06 21 6.64 21 6.319 20.837C6.03651 20.6931 5.80685 20.4635 5.663 20.181C5.5 19.861 5.5 19.441 5.5 18.6V6.4Z" stroke="black"/>
+<path d="M12.5 4V7.6C12.5 8.44 12.5 8.86 12.664 9.181C12.8076 9.46334 13.0369 9.69298 13.319 9.837C13.639 10 14.059 10 14.9 10H18.5" stroke="black"/>
+<rect x="8" y="11.5" width="8" height="1" rx="0.5" fill="black"/>
+<rect x="8" y="14.5" width="8" height="1" rx="0.5" fill="black"/>
+<rect x="8" y="17.5" width="8" height="1" rx="0.5" fill="black"/>
+</svg>

+ 5 - 0
src/assets/icon/classroomObservation/videoFile_icon.svg

@@ -0,0 +1,5 @@
+<svg width="24" height="25" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5.5 6.4C5.5 5.56 5.5 5.14 5.663 4.819C5.80685 4.53651 6.03651 4.30685 6.319 4.163C6.639 4 7.059 4 7.9 4H12.506C12.873 4 13.056 4 13.229 4.041C13.3837 4.07833 13.528 4.13833 13.662 4.221C13.814 4.314 13.943 4.444 14.202 4.703L17.797 8.297C18.057 8.557 18.187 8.687 18.279 8.837C18.3617 8.973 18.4217 9.11767 18.459 9.271C18.5 9.444 18.5 9.627 18.5 9.994V18.6C18.5 19.44 18.5 19.86 18.337 20.181C18.1931 20.4635 17.9635 20.6931 17.681 20.837C17.361 21 16.941 21 16.1 21H7.9C7.06 21 6.64 21 6.319 20.837C6.03651 20.6931 5.80685 20.4635 5.663 20.181C5.5 19.861 5.5 19.441 5.5 18.6V6.4Z" stroke="black"/>
+<path d="M12.5 4V7.6C12.5 8.44 12.5 8.86 12.664 9.181C12.8076 9.46334 13.0369 9.69298 13.319 9.837C13.639 10 14.059 10 14.9 10H18.5" stroke="black"/>
+<path d="M14.2222 14.375V12.9167C14.2222 12.8062 14.1754 12.7002 14.092 12.622C14.0087 12.5439 13.8957 12.5 13.7778 12.5H8.44444C8.32657 12.5 8.21352 12.5439 8.13017 12.622C8.04683 12.7002 8 12.8062 8 12.9167V17.0833C8 17.1938 8.04683 17.2998 8.13017 17.378C8.21352 17.4561 8.32657 17.5 8.44444 17.5H13.7778C13.8957 17.5 14.0087 17.4561 14.092 17.378C14.1754 17.2998 14.2222 17.1938 14.2222 17.0833V15.625L16 17.2917V12.7083L14.2222 14.375Z" fill="black"/>
+</svg>

+ 111 - 99
src/common/axios.config.js

@@ -11,128 +11,140 @@ let cancel; // 用于存储取消请求的方法
 
 //POST传参序列化(添加请求拦截器)
 axios.interceptors.request.use((config) => {
-    //在发送请求之前做某件事
-    let token = sessionStorage.getItem('access_token') || ""  //获取token
-    if (token != "") {
-        config.headers = {
-            'access-token': token,
-            'Content-Type': 'application/x-www-form-urlencoded'
-        }
+  //在发送请求之前做某件事
+  let token = sessionStorage.getItem('access_token') || ""  //获取token
+  if (token != "") {
+    config.headers = {
+      'access-token': token,
+      'Content-Type': 'application/x-www-form-urlencoded'
     }
-    // if (config.data && config.data[0].post == '1' && config.method === 'post') {
-    //     // config.headers.post['Content-Type'] = 'application/json;charset=UTF-8';
-    //     // config.data = config.data//序列化post 参数
-    //     config.data = 'mode=' + (Object.values(config.data[0]).join(','))//序列化post 参数
-    // } else if (config.method === 'post') {
-    //     config.data = qs.stringify(config.data)//序列化post 参数
-    // }
-    if (config.url === 'https://gpt.cocorobo.cn/search_image' || config.url === 'https://gpt.cocorobo.cn/chat' || config.url === 'https://gpt4.cocorobo.cn/create_free_assistants' || config.url === 'https://gpt4.cocorobo.cn/assistants_completion_response') {
-        config.data = config.data//序列化post 参数
-    } else if (config.url.indexOf('https://r2rserver.cocorobo.cn') != -1) {
-        config.headers = {
-            'Content-Type': 'application/json',
-        }
-    } else if (config.url.indexOf('https://gpt4.cocorobo.cn/') != -1 || config.url.indexOf('https://claude3.cocorobo.cn/') != -1 || config.url.indexOf('llm.cocorobo.cn/') != -1 || config.url.indexOf('https://appapi.cocorobo.cn/')!=-1) {
-        config.headers = {
-            'Content-Type': 'application/json',
-        }
-    } else if (config.data && config.data[0].post == '1' && config.method === 'post') {
-        config.data = 'mode=' + (Object.values(config.data[0]).join(','))//序列化post 参数
-    } else if (config.method === 'post') {
+  }
+  // if (config.data && config.data[0].post == '1' && config.method === 'post') {
+  //     // config.headers.post['Content-Type'] = 'application/json;charset=UTF-8';
+  //     // config.data = config.data//序列化post 参数
+  //     config.data = 'mode=' + (Object.values(config.data[0]).join(','))//序列化post 参数
+  // } else if (config.method === 'post') {
+  //     config.data = qs.stringify(config.data)//序列化post 参数
+  // }
+  if (config.url === 'https://gpt.cocorobo.cn/search_image' || config.url === 'https://gpt.cocorobo.cn/chat' || config.url === 'https://gpt4.cocorobo.cn/create_free_assistants' || config.url === 'https://gpt4.cocorobo.cn/assistants_completion_response') {
+    config.data = config.data//序列化post 参数
+  } else if (config.url.indexOf('https://r2rserver.cocorobo.cn') != -1) {
+    config.headers = {
+      'Content-Type': 'application/json',
+    }
+  } else if (config.url.indexOf('https://gpt4.cocorobo.cn/') != -1 || config.url.indexOf('https://claude3.cocorobo.cn/') != -1 || config.url.indexOf('llm.cocorobo.cn/') != -1 || config.url.indexOf('https://appapi.cocorobo.cn/') != -1) {
+    config.headers = {
+      'Content-Type': 'application/json',
+    }
+  } else if (config.url.indexOf('https://dify.cocorobo.cn') != -1) {
+    if (config.url.indexOf('?key=role') != -1) {
+      config.headers = {
+        Authorization: `Bearer app-TonzLPv7rPG0EtnFKszOWjwt`,
+        "Content-Type": "application/json"
+      }
+    } else if (config.url.indexOf('?key=code') != -1) {
+      config.headers = {
+        Authorization: `Bearer app-zOMxBqyEKoJSvW10e5SS0kgj`,
+        "Content-Type": "application/json"
+      }
+    }
+  } else if (config.data && config.data[0].post == '1' && config.method === 'post') {
+    config.data = 'mode=' + (Object.values(config.data[0]).join(','))//序列化post 参数
+  } else if (config.method === 'post') {
 
-        const encoded = {};
-        for (const key in config.data[0]) {
-            if (Object.hasOwnProperty.call(config.data[0], key)) {
-                encoded[key] = encodeURIComponent(config.data[0][key]);
-            }
-        }
-        config.data = qs.stringify([encoded]) //序列化post 参数
-    } else {
-        const encoded = {};
-        for (const key in config.data) {
-            if (Object.hasOwnProperty.call(config.data, key)) {
-                encoded[key] = encodeURIComponent(config.data[key]);
-            }
-        }
-        config.data = encoded
+    const encoded = {};
+    for (const key in config.data[0]) {
+      if (Object.hasOwnProperty.call(config.data[0], key)) {
+        encoded[key] = encodeURIComponent(config.data[0][key]);
+      }
+    }
+    config.data = qs.stringify([encoded]) //序列化post 参数
+  } else {
+    const encoded = {};
+    for (const key in config.data) {
+      if (Object.hasOwnProperty.call(config.data, key)) {
+        encoded[key] = encodeURIComponent(config.data[key]);
+      }
     }
+    config.data = encoded
+  }
 
-    return config;
+  return config;
 }, (error) => {
-    console.log('错误的传参')
+  console.log('错误的传参')
 
-    return Promise.reject(error);
+  return Promise.reject(error);
 });
 //返回状态判断(添加响应拦截器)
 axios.interceptors.response.use((res) => {
-    //对响应数据做些事
-    if (!res.data.success) {
-        let newToken = res.data.token    //成功后更新token
-        localStorage.setItem('access_token', newToken)
+  //对响应数据做些事
+  if (!res.data.success) {
+    let newToken = res.data.token    //成功后更新token
+    localStorage.setItem('access_token', newToken)
 
-    }
-    return res;
+  }
+  return res;
 }, (error) => {
-    if (axios.isCancel(error)) {
-        console.log('请求已取消', error.message);
-    } else if (error.response.data.status == '401') {    //如果token 过期 则跳转到登录页面
-        this.$router.push('/login');
-    }
-    return Promise.reject(error);
+  if (axios.isCancel(error)) {
+    console.log('请求已取消', error.message);
+  } else if (error.response.data.status == '401') {    //如果token 过期 则跳转到登录页面
+    this.$router.push('/login');
+  }
+  return Promise.reject(error);
 });
 //返回一个Promise(发送post请求)
 function post(url, params, source) {
-    return new Promise((resolve, reject) => {
-        axios.post(url, params, source ? { cancelToken: source.token } : '')
-            .then(response => {
-                resolve(response);
-            }, err => {
-                reject(err);
-            })
-            .catch((error) => {
-                reject(error)
-            })
-    })
+  return new Promise((resolve, reject) => {
+    axios.post(url, params, source ? { cancelToken: source.token } : '')
+      .then(response => {
+        resolve(response);
+      }, err => {
+        reject(err);
+      })
+      .catch((error) => {
+        reject(error)
+      })
+  })
 }
 
 //返回一个Promise(发送put请求)
 function put(url, params, source) {
-    return new Promise((resolve, reject) => {
-        axios.put(url, params, source ? { cancelToken: source.token } : '')
-            .then(response => {
-                resolve(response);
-            }, err => {
-                reject(err);
-            })
-            .catch((error) => {
-                reject(error)
-            })
-    })
+  return new Promise((resolve, reject) => {
+    axios.put(url, params, source ? { cancelToken: source.token } : '')
+      .then(response => {
+        resolve(response);
+      }, err => {
+        reject(err);
+      })
+      .catch((error) => {
+        reject(error)
+      })
+  })
 }
 
 
 ////返回一个Promise(发送get请求)
 function get(url, param, source) {
-    return new Promise((resolve, reject) => {
-        let cancelToken = source ? source.token : ''
-        axios.get(url, { params: param, cancelToken  })
-            .then(response => {
-                resolve(response)
-            }, err => {
-                reject(err)
-            })
-            .catch((error) => {
-                reject(error)
-            })
-    })
+  return new Promise((resolve, reject) => {
+    let cancelToken = source ? source.token : ''
+    axios.get(url, { params: param, cancelToken })
+      .then(response => {
+        resolve(response)
+      }, err => {
+        reject(err)
+      })
+      .catch((error) => {
+        reject(error)
+      })
+  })
 }
 export default {
-    get,
-    post,
-    put,
-    setCancelSource: () => {
-        // 每次创建新的请求时,可以调用此方法以创建新的取消令牌
-        cancel = CancelToken.source();
-        return cancel;
-    }
+  get,
+  post,
+  put,
+  setCancelSource: () => {
+    // 每次创建新的请求时,可以调用此方法以创建新的取消令牌
+    cancel = CancelToken.source();
+    return cancel;
+  }
 }

+ 414 - 33
src/components/pages/classroomObservation/components/chatArea.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="chatArea" v-loading="loading">
+  <div class="chatArea">
     <div class="m-operation">
       <div>实时转录</div>
       <div>{{ createTime }}</div>
@@ -64,17 +64,9 @@
         v-if="pageStatus == 2 && !showIndexPage && editorBarData.type == '0'"
         v-loading="uploadFileLoading"
       >
-        <div
-          class="wavGetTextProgress"
-          v-if="wavGetTextLoading"
-          style="position: absolute; bottom: 20px; right: 285px; z-index: 10002"
-        >
-          <el-progress
-            :text-inside="true"
-            :stroke-width="26"
-            :percentage="wavGetTextProgress"
-          ></el-progress>
-        </div>
+			  <div class="wavGetTextProgress" v-if="wavGetTextLoading" style="position: absolute; bottom: 20px; right: 285px; z-index: 10002">
+					<el-progress :text-inside="true" :stroke-width="26" :percentage="wavGetTextProgress"></el-progress>
+				</div>
         <el-button
           style="position: absolute; bottom: 20px; right: 185px; z-index: 10002"
           size="small"
@@ -83,6 +75,32 @@
           @click.stop="stopWavGetText()"
           >终止转译</el-button
         >
+        <el-popover placement="top" trigger="click" v-if="!wavGetTextLoading">
+          <div style="display: flex;">
+            <el-input
+              size="small"
+              style="width: 150px;margin-right: 10px;"
+              v-model="replace1"
+              placeholder="替换之前的文字"
+            ></el-input>
+            <el-input
+              size="small"
+              style="width: 150px;margin-right: 10px;"
+              v-model="replace2"
+              placeholder="替换之后的文字"
+            ></el-input>
+            <el-button type="primary" size="small" @click="replaceText()"
+              >替换</el-button
+            >
+          </div>
+          <el-button
+            slot="reference"
+            style="position: absolute; bottom: 20px; right: 185px; z-index: 10002"
+            size="small"
+            type="primary"
+            >文字替换</el-button
+          >
+        </el-popover>
         <el-popover placement="top" trigger="hover">
           <el-button size="small" @click.stop="startContinuousJobs('role')">
             说话人编码
@@ -769,13 +787,15 @@ export default {
       showTeacherVoiceprintBox: false,
       teacherVoiceprintList: [],
       chosenVoiceprint: [],
-      recorderProvider: "shengyang",
+      recorderProvider: "microsoft",
       shengyangContext: {
         recorder: null,
         ws: null
       },
       userName: "",
-      curRequestController: null
+      curRequestController: null,
+      replace1: "", //替换1
+      replace2: "" //替换2
     };
   },
   computed: {
@@ -1599,6 +1619,13 @@ ${JSON.stringify(_list)}
         console.log("音频时长", _duration);
       });
 
+      let _startTime = 0;
+      let _endTime = 0;
+      let _getRoleList = [];
+      let _roleList = {};
+      let _getRoleLoading = false;
+      let _getBehavioralCodingLoading = false;
+      let _behavioralCodingList = [];
       try {
         let iiframe = this.$refs["iiframe"];
         let _this = this;
@@ -1644,19 +1671,182 @@ ${JSON.stringify(_list)}
           _this.showGetTextLoading = true;
           let privText = e.privText;
           let privSpeakerId = e.privSpeakerId;
-          console.log("👇转译对象👇");
-          console.log(e);
-          console.log("👇转译结果👇");
-          console.log(privText);
-          if (!privText) return;
+          let privDuration = e.privDuration;
+          let privOffset = e.privOffset;
+          console.log("👉转译对象👉", e);
+          console.log("👉转译结果👉", privText);
+          if (!privText || !privSpeakerId || privSpeakerId == "Unknown") {
+            return;
+          }
+
+          if (_roleList[privSpeakerId]) {
+            privSpeakerId = _roleList[privSpeakerId];
+          } else {
+            _getRoleList.push({
+              role: privSpeakerId,
+              content: privText
+            });
+          }
+
+          _behavioralCodingList.push({
+            index: textList.length,
+            role: privSpeakerId,
+            content: privText,
+            code: ""
+          });
+
+          console.log("roleList", _roleList);
+          console.log("getRoleList", _getRoleList);
+          console.log("behavioralCodingList", _behavioralCodingList);
+          if (_getRoleList.length >= 10 && !_getRoleLoading) {
+            try {
+              let params = {
+                inputs: {
+                  options: "老师,学生",
+                  rows: JSON.stringify(
+                    _getRoleList.map(i => {
+                      return { content: i.content, role: i.role };
+                    })
+                  )
+                },
+                response_mode: "blocking",
+                user: _this.userId
+              };
+              _getRoleLoading = true;
+              _this
+                .getWavRoleList(params)
+                .then(res => {
+                  let _runData = res.data.data;
+                  let _runResult = _runData.outputs.result;
+                  let _numRole = [];
+
+                  _runResult.forEach((txt, index) => {
+                    let _oldRole = _getRoleList[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) {
+                      _roleList[i.role] = "老师";
+                    } else if (i.t < i.s) {
+                      _roleList[i.role] = "学生";
+                    }
+                  });
+                  //已经有的role
+                  let roleKeys = Object.keys(_roleList);
+
+                  textList.forEach(i => {
+                    if (roleKeys.includes(i.role)) {
+                      i.role = _roleList[i.role];
+                    }
+                  });
+
+                  //同时更新
+                  _behavioralCodingList.forEach(i=>{
+                    if(roleKeys.includes(i.role)){
+                      i.role = _roleList[i.role]
+                    }
+                  })
+
+                  _getRoleList = _getRoleList.filter(
+                    i => !roleKeys.includes(i.role)
+                  );
+                  _getRoleLoading = false;
+                })
+                .catch(err => {
+                  console.log("获取说话人编码失败", err);
+                  _getRoleLoading = false;
+                });
+            } catch (error) {
+              console.log("说话人编码失败", error);
+            }
+          }
+          console.log("behavioralCodingListIf", (_behavioralCodingList.filter(
+              i => (i.role.indexOf("Guest") == -1 && i.code == "")
+            )));
+          if (
+            _behavioralCodingList.filter(
+              i => i.role.indexOf("Guest") === -1 && i.code === ""
+            ).length >= 10 &&
+            !_getBehavioralCodingLoading
+          ) {
+            try {
+              const _behavioralCodingListTemp = _behavioralCodingList
+                .filter(i => i.role.indexOf("Guest") == -1 && i.code == "")
+                .slice(0, 10);
+
+              console.log("behavioralCodingListTemp", _behavioralCodingListTemp);
+              const params = {
+                inputs: {
+                  rows: JSON.stringify(
+                    _behavioralCodingListTemp.map(i => ({
+                      content: i.content,
+                      role: i.role
+                    }))
+                  ),
+                  options: "老师讲课,老师提问或点名,老师板书或操作,老师评价或反馈,老师其他,学生发言,学生小组活动,学生自主学习,学生汇报分享,学生其他",
+                  attention: "- 先根据说话人角色判断,再在对应角色的选项中选择选项\n- 如果没有合适的选项,默认使用`老师其他`或者`学生其他`"
+                },
+                response_mode: "blocking",
+                user: _this.userId
+              };
+
+              _getBehavioralCodingLoading = true;
+              _this
+                .getBehavioralCoding(params)
+                .then(res => {
+                  const _runResult = res.data.data.outputs.result;
+                  _behavioralCodingListTemp.forEach((item, index) => {
+                    const foundItem = _behavioralCodingList.find(i => i.index === item.index);
+                    if (foundItem) {
+                      foundItem.code = _runResult[index];
+                      textList[item.index].code = foundItem.code;
+                    }
+                  });
+                  _getBehavioralCodingLoading = false;
+                })
+                .catch(err => {
+                  console.log("获取行为编码错误", err);
+                  _getBehavioralCodingLoading = false;
+                });
+            } catch (error) {
+              console.log("获取行为编码错误", error);
+              _getBehavioralCodingLoading = false;
+            }
+          }
+
+          _endTime = (privOffset + privDuration) / 10000000;
           textList.push({
             value: privText,
-            startTime: "",
-            endTime: "",
-            time: ""
+            startTime: _this.updateRecordedTime({ duration: _startTime }),
+            endTime: _this.updateRecordedTime({ duration: _endTime }),
+            time: _this.updateRecordedTime({ duration: _endTime - _startTime }),
+            role: privSpeakerId,
+            code: ""
           });
+
+          _startTime = _endTime;
+
           _this.transcriptionData.content += privText;
 
+          // console.log(textList);
+
           let _result = `
 				<table
 						border="0"
@@ -1679,12 +1869,12 @@ ${JSON.stringify(_list)}
           textList.forEach((item, index) => {
             _result += `<tr>
 							<td>${index + 1}</td>
-							<td></td>
-							<td></td>
+							<td>${item.startTime}</td>
+							<td>${item.endTime}</td>
 							<td>${item.value}</td>
-							<td></td>
-							<td></td>
-							<td></td>
+							<td>${item.time}</td>
+							<td>${item.role}</td>
+							<td>${item.code}</td>
 						</tr>`;
           });
           _result += `
@@ -1700,10 +1890,162 @@ ${JSON.stringify(_list)}
 					</tbody>
 				</table>`;
           _this.editorBarData.content = _result;
+          this.$forceUpdate();
           // _this.editorBarData.content += privText;
         };
 
-        iiframe.contentWindow.onSessionStopped = function(e) {
+        iiframe.contentWindow.onSessionStopped = async function(e) {
+          let _flag = false;
+          if (_getRoleList.length > 0) {
+            _flag = true;
+            await _this
+              .getWavRoleList({
+                inputs: {
+                  options: "老师,学生",
+                  rows: JSON.stringify(
+                    _getRoleList.map(i => {
+                      return { content: i.content, role: i.role };
+                    })
+                  )
+                },
+                response_mode: "blocking",
+                user: _this.userId
+              })
+              .then(res => {
+                let _runData = res.data.data;
+                let _runResult = _runData.outputs.result;
+                let _numRole = [];
+
+                _runResult.forEach((txt, index) => {
+                  let _oldRole = _getRoleList[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) {
+                    _roleList[i.role] = "老师";
+                  } else if (i.t < i.s) {
+                    _roleList[i.role] = "学生";
+                  }
+                });
+                //已经有的role
+                let roleKeys = Object.keys(_roleList);
+
+                textList.forEach(i => {
+                  if (roleKeys.includes(i.role)) {
+                    i.role = _roleList[i.role];
+                  }
+                });
+
+                _getRoleList = _getRoleList.filter(
+                  i => !roleKeys.includes(i.role)
+                );
+                _getRoleLoading = false;
+              })
+              .catch(err => {
+                console.log("最后的获取说话人身份失败", err);
+              });
+          }
+          if (
+            _behavioralCodingList.some(
+              i => i.role.indexOf("Guest") === -1 && i.code === ""
+            )
+          ) {
+            _flag = true;
+            const _behavioralCodingListTemp = _behavioralCodingList
+                .filter(i => i.role.indexOf("Guest") == -1 && i.code == "")
+              const params = {
+                inputs: {
+                  rows: JSON.stringify(
+                    _behavioralCodingListTemp.map(i => ({
+                      content: i.content,
+                      role: i.role
+                    }))
+                  ),
+                  options: "老师讲课,老师提问或点名,老师板书或操作,老师评价或反馈,老师其他,学生发言,学生小组活动,学生自主学习,学生汇报分享,学生其他",
+                  attention: "- 先根据说话人角色判断,再在对应角色的选项中选择选项\n- 如果没有合适的选项,默认使用`老师其他`或者`学生其他`"
+                },
+                response_mode: "blocking",
+                user: _this.userId
+              };
+              await _this
+                .getBehavioralCoding(params)
+                .then(res => {
+                  const _runResult = res.data.data.outputs.result;
+                  _behavioralCodingListTemp.forEach((item, index) => {
+                    const foundItem = _behavioralCodingList.find(i => i.index === item.index);
+                    if (foundItem) {
+                      foundItem.code = _runResult[index];
+                      textList[item.index].code = foundItem.code;
+                    }
+                  });
+                })
+                .catch(err => {
+                  console.log("获取行为编码错误", err);
+                });
+          }
+
+          if (_flag) {
+            let _result = `
+				<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>
+				`;
+            textList.forEach((item, index) => {
+              _result += `<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>`;
+            });
+            _result += `
+				<tr>
+							<td></td>
+							<td></td>
+							<td></td>
+							<td></td>
+							<td></td>
+							<td></td>
+							<td></td>
+						</tr>
+					</tbody>
+				</table>`;
+            _this.editorBarData.content = _result;
+          }
           _this.wavGetTextProgress = 100;
           if (_this.wavFileGetTextLoading) _this.$message.success("转译完成");
           _this.showGetTextLoading = false;
@@ -1711,10 +2053,6 @@ ${JSON.stringify(_list)}
           _this.saveEditorBar();
         };
 
-        //iiframe.contentWindow.doContinuousPronunciationAssessment("", {
-        //  files: [_file]
-        //});
-
         iiframe.contentWindow.ConversationTranscriber({
           files: [_file]
         });
@@ -1724,6 +2062,31 @@ ${JSON.stringify(_list)}
         this.showGetTextLoading = false;
       }
     },
+
+    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 => {
+            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 => {
+            reject(err);
+          });
+      });
+    },
     stopWavGetText() {
       if (this.wavGetTextLoading) {
         try {
@@ -3018,8 +3381,26 @@ ${JSON.stringify(_list)}
       this.shengyangContext.recorder = null;
       this.shengyangContext.ws = null;
       // TODO
-    }
+    },
     // ============ end 声扬录音转译
+    //替换文字
+    replaceText() {
+      if (!this.tid) return;
+      if (!this.replace1.trim() || !this.replace2.trim()) {
+        return this.$message.error("请输入内容");
+      }
+
+      let _replace1 = this.replace1.trim();
+      let _replace2 = this.replace2.trim();
+      console.log(_replace1, _replace2);
+      // console.log(this.editorBarData)
+      this.editorBarData.content = this.editorBarData.content.replace(
+        new RegExp(_replace1, "g"),
+        _replace2
+      );
+      this.$forceUpdate();
+      // this.saveEditorBar();
+    }
   },
   mounted() {
     this.loadVoiceprints();

+ 5 - 2
src/components/pages/classroomObservation/components/previewVideoDialog.vue

@@ -16,8 +16,12 @@
 		  title="视频预览"
 		  :visible.sync="dialogVisible"
 			show-close
+      :modal="true"
+      :close-on-click-modal="true"
 			top="3vh"
 			width="60vw"
+      class="videoPreview"
+      :append-to-body="true"
 			:before-close="handleClose">
 			<div class="video-player-box" v-if="option.sources!=''">
 				<video-player
@@ -62,7 +66,7 @@ export default {
     };
   },
   watch: {
-    
+
   },
   methods: {
     open(url) {
@@ -81,7 +85,6 @@ export default {
 </script>
 
 <style scoped>
-
 .video-player-box{
 	width: 100%;
 	height: 100%;

+ 768 - 0
src/components/pages/classroomObservation/dialog/batchCreationClassDialog.vue

@@ -0,0 +1,768 @@
+<template>
+  <div class="batchCreationClassDialog">
+    <el-dialog
+      :visible.sync="show"
+      width="100%"
+      height="100%"
+      fullscreen
+      :modal="false"
+      :close-on-click-modal="true"
+      :show-close="false"
+    >
+      <div class="box">
+        <div class="b_back">
+          <div class="b_b_left" @click="close()">
+            <img src="../../../../assets/icon/classroomObservation/right.svg" />
+            <span>返回</span>
+          </div>
+          <div class="b_b_right">
+            <el-button type="primary" size="small" @click="uploadFile"
+              >上传文件</el-button
+            >
+          </div>
+        </div>
+        <div class="b_head">
+          <span>已上传的课堂实录</span>
+        </div>
+        <div class="b_nav">
+          <div class="b_n_left">
+            <span
+              :class="{ b_n_l_active: selectStatus == item.value }"
+              v-for="item in statusList"
+              :key="item.value"
+              @click="changeStatus(item.value)"
+              >{{ item.label }}</span
+            >
+          </div>
+          <div class="b_n_right">
+            <span>总计{{ dataList.length }}个</span>
+          </div>
+        </div>
+        <div class="b_bottom">
+          <div class="b_b_operation">
+            <div class="b_b_o_left">
+              <span
+                >当前进度 {{ progress }}/{{
+                  dataList.filter(
+                    i =>
+                      selectStatus == "99" ||
+                      statusList
+                        .find(i => i.value == selectStatus)
+                        .allow.includes(i.state)
+                  ).length
+                }}</span
+              >
+            </div>
+            <div class="b_b_o_right">
+              <div>
+                <el-checkbox v-model="selectAll" :indeterminate="indeterminate"
+                  >全选</el-checkbox
+                >
+              </div>
+              <span>
+                <img
+                  @click="delSelectBtn"
+                  src="../../../../assets/icon/classroomObservation/del2.svg"
+                />
+              </span>
+            </div>
+          </div>
+          <div class="b_b_list">
+            <batchClassCard
+              v-for="item in dataList"
+              :data="item"
+              :isSelect="selectList.includes(item.id)"
+              :key="item.id"
+              v-show="
+                selectStatus === '99' ||
+                  statusList
+                    .find(i => i.value === selectStatus)
+                    .allow.includes(item.status)
+              "
+              @changeChecked="changeCardChecked"
+              @editBaseMessage="editBaseMessage"
+              @changeData="changeData"
+            />
+          </div>
+        </div>
+      </div>
+      <uploadFileToCreateClassDialog
+        ref="uploadFileToCreateClassDialogRef"
+        @success="uploadFileSuccess"
+      />
+      <editBaseMessageDialog
+        ref="editBaseMessageDialogRef"
+        @success="editBaseMessageSuccess"
+      />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import batchClassCard from "../newComponents/batchClassCard.vue";
+import uploadFileToCreateClassDialog from "./uploadFileToCreateClassDialog.vue";
+import editBaseMessageDialog from "./editBaseMessageDialog.vue";
+import { v4 as uuidv4 } from "uuid";
+export default {
+  components: {
+    batchClassCard,
+    uploadFileToCreateClassDialog,
+    editBaseMessageDialog
+  },
+  data() {
+    return {
+      statusList: [
+        { label: "全部", value: "99", allow: [] },
+        { label: "已完成", value: "2", allow: ["2"] },
+        { label: "处理中", value: "1", allow: ["1"] },
+        { label: "等待处理", value: "0", allow: ["0"] },
+        { label: "停止中",value:"3",allow:['3']}
+      ],
+      userId: this.$route.query["userid"],
+      selectStatus: "99",
+      show: false,
+      selectList: [],
+      dataList: []
+    };
+  },
+  computed: {
+    selectAll: {
+      get() {
+        let _result = false;
+
+        let _data = this.dataList.filter(
+          i =>
+            this.selectStatus === "99" ||
+            this.statusList
+              .find(i2 => i2.value === this.selectStatus)
+              .allow.includes(i.status)
+        );
+
+        if (_data.length > 0 && _data.length === this.selectList.length) {
+          _result = true;
+        }
+        return _result;
+      },
+      set(value) {
+        if (value) {
+          this.selectList = this.dataList
+            .filter(
+              i =>
+                this.selectStatus === "99" ||
+                this.statusList
+                  .find(i2 => i2.value === this.selectStatus)
+                  .allow.includes(i.status)
+            )
+            .map(i => i.id);
+          this.$forceUpdate();
+        } else {
+          this.selectList = [];
+        }
+      }
+    },
+    indeterminate() {
+      let _result = false;
+
+      let _data = this.dataList.filter(
+        i =>
+          this.selectStatus === "99" ||
+          this.statusList
+            .find(i2 => i2.value === this.selectStatus)
+            .allow.includes(i.status)
+      );
+
+      if (_data.length > this.selectList.length && this.selectList.length > 0) {
+        _result = true;
+      }
+
+      return _result;
+    },
+    progress() {
+      return this.dataList.filter(
+        i =>
+          i.status == "2" &&
+          (this.selectStatus == "99" ||
+            this.statusList
+              .find(i2 => i2.value == this.selectStatus)
+              .allow.includes(i.state))
+      ).length;
+    }
+  },
+  watch: {
+    selectList(newValue, oldValue) {
+      console.log(newValue, oldValue);
+    }
+  },
+  methods: {
+    open() {
+      this.show = true;
+    },
+    close() {
+      this.show = false;
+    },
+    init() {},
+    changeStatus(newValue) {
+      this.selectStatus = newValue;
+      this.selectList = [];
+    },
+    uploadFile() {
+      this.$refs.uploadFileToCreateClassDialogRef.open();
+    },
+    //批量上传文件成功
+    async uploadFileSuccess(data) {
+      this.$refs.uploadFileToCreateClassDialogRef.close();
+      let { fileList, automaticCoding, analysisTemplate } = data;
+      this.loading = true;
+
+      let fileDataList = fileList.map(i => i.successData);
+
+      let _tempData = await this.getTemplateData(analysisTemplate[1]);
+
+      let _analysisList = JSON.parse(_tempData.tips);
+      let tagList = _analysisList.find(i => i.isOtherData);
+
+      if (!tagList) {
+        tagList = [
+          { value: 0, name: "通用课堂分析", loading: false },
+          { value: 1, name: "学科课堂分析", loading: false },
+          { value: 2, name: "扩展分析", loading: false }
+        ];
+      }
+
+      let batch = uuidv4();
+
+      _analysisList = _analysisList.filter(
+        i => !i.isOtherData && i.jsonData.name != "词频词汇分析"
+      );
+
+      let promiseList = [];
+
+      fileDataList.forEach(i => {
+        let data = {
+          id: uuidv4(),
+          create_at: "2025-05-07 16.05.03",
+          remarks: "备注",
+          batch: batch,
+          status: "3",
+          createId: "",
+          jsonData: {
+            file_ids: "",
+            baseMessage: {
+              courseName: i.name,
+              teacherName: "",
+              time: "",
+              grade: "",
+              subject: "",
+              textbook: "",
+              studentNum: 0,
+              imageList: {
+                fileList: [],
+                fileList1: [],
+                fileList2: [],
+                fileList3: [],
+                NephogramList: [],
+                videoList: []
+              }
+            },
+            tagList: tagList,
+            steps: [],
+            analysisList: _analysisList,
+            fileData: i,
+            automaticCoding: automaticCoding
+          }
+        };
+
+        if (i.type == "text/plain") {
+          data.jsonData.steps = [
+            {
+              type: "uploadFile",
+              text: "上传文件",
+              status: "1"
+            },
+            {
+              type: "transcription",
+              text: "文本转录",
+              status: "0",
+              progress: "0"
+            },
+            {
+              type: "getFileIds",
+              text: "获取文件fileid",
+              status: "0"
+            },
+            {
+              type: "generateReport",
+              text: "生成报告",
+              status: "0",
+              progress: "0"
+            }
+          ];
+        } else if (i.type == "audio/wav") {
+          data.jsonData.baseMessage.imageList.fileList = [
+            {
+              name: i.name,
+              status: "success",
+              url: i.url,
+              uid: "1"
+            }
+          ];
+          data.jsonData.steps = [
+            {
+              type: "uploadFile",
+              text: "上传文件",
+              status: "1"
+            },
+            {
+              type: "transcription",
+              text: "文本转录",
+              status: "0",
+              progress: "0"
+            },
+            {
+              type: "automaticCoding",
+              text: "自动编码",
+              status: "0",
+              progress: "0"
+            },
+            {
+              type: "getFileIds",
+              text: "获取文件fileid",
+              status: "0"
+            },
+            {
+              type: "generateReport",
+              text: "生成报告",
+              status: "0",
+              progress: "0"
+            }
+          ];
+        } else if (i.type == "video/mp4") {
+          data.jsonData.baseMessage.imageList.videoList = [
+            {
+              name: i.name,
+              status: "success",
+              url: i.url,
+              uid: "1"
+            }
+          ];
+          data.jsonData.steps = [
+            {
+              type: "uploadFile",
+              text: "上传文件",
+              status: "1"
+            },
+            {
+              type: "getVideoVoice",
+              text: "视频提取音频",
+              status: "0",
+              progress: "0"
+            },
+            {
+              type: "transcription",
+              text: "文本转录",
+              status: "0",
+              progress: "0"
+            },
+            {
+              type: "automaticCoding",
+              text: "自动编码",
+              status: "0",
+              progress: "0"
+            },
+            {
+              type: "getFileIds",
+              text: "获取文件fileid",
+              status: "0"
+            },
+            {
+              type: "generateReport",
+              text: "生成报告",
+              status: "0",
+              progress: "0"
+            }
+          ];
+        }
+
+        promiseList.push(
+          new Promise((resolve, reject) => {
+            const params = [
+              {
+                remarks: data.remarks,
+                status: data.status,
+                json: JSON.stringify(data.jsonData),
+                batch: data.batch,
+                createId: data.createId,
+                userId: this.userId
+              }
+            ];
+            console.log(params);
+            this.ajax
+              .post(this.$store.state.api + "insert_classroomTask", params)
+              .then(res => {
+                const _data = res.data[0][0];
+                if (_data.id) {
+                  data.id = _data.id;
+                  this.dataList.push(data);
+                }
+                resolve();
+              })
+              .catch(err => {
+                console.error(err);
+                reject(err);
+              });
+          })
+        );
+      });
+      console.log(promiseList);
+      Promise.all(promiseList)
+        .then(res => {
+          this.$message.success("创建成功");
+        })
+        .catch(err => {
+          console.log("创建出错", err);
+        });
+    },
+    //获取模板的数据
+    getTemplateData(tid) {
+      return new Promise(resolve => {
+        let params = {
+          uid: this.userId,
+          cid: tid,
+          st: 0
+        };
+        this.ajax
+          .get(this.$store.state.api + "selectClassroomTemplateDetail", params)
+          .then(res => {
+            let _data = res.data[0][0];
+            resolve(_data);
+          });
+      });
+    },
+    changeCardChecked({ type = 0, id }) {
+      if (type === 0 && !this.selectList.includes(id)) {
+        this.selectList.push(id);
+      } else if (type === 1 && this.selectList.push(id)) {
+        this.selectList = this.selectList.filter(i => i != id);
+      }
+    },
+    //修改课堂的baseMessage按钮
+    editBaseMessage(id) {
+      let _data = this.dataList.find(i => i.id === id);
+      if (_data) {
+        this.$refs.editBaseMessageDialogRef.open({
+          editId: _data.id,
+          message: _data.jsonData.baseMessage
+        });
+      } else {
+        this.$message.error("未查询到该数据");
+      }
+    },
+    //修改课堂的baseMessage
+    editBaseMessageSuccess({ editId, message }) {
+      if (editId) {
+        this.dataList.find(i => i.id === editId).jsonData.baseMessage = message;
+        this.$forceUpdate();
+        this.$refs.editBaseMessageDialogRef.close();
+        this.updateTask(editId);
+      }
+    },
+    // 删除选择的数据
+    delSelectBtn() {
+      let _delList = this.dataList.filter(
+        i =>
+          this.selectList.includes(i.id) &&
+          (this.selectStatus === "99" ||
+            this.statusList
+              .find(i2 => i2.value === this.selectStatus)
+              .allow.includes(i.status))
+      );
+
+      if (_delList.length <= 0) return this.$message.info("请先选择列表");
+      this.$confirm("您确定要删除选中的数据吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      })
+        .then(() => {
+          // 确定删除
+          let params = [
+            {
+              idList: _delList.map(i => i.id).join(","),
+              userId: this.userId
+            }
+          ];
+
+          this.ajax
+            .post(this.$store.state.api + "delete_classroomTask", params)
+            .then(res => {
+              if (res.data > 0) {
+                this.$message.success("删除成功");
+                this.dataList = this.dataList.filter(
+                  i => !_delList.find(del => del.id === i.id)
+                );
+                this.selectList = [];
+              }
+            })
+            .catch(err => {
+              console.log(err);
+              this.$message.error("删除失败");
+            });
+        })
+        .catch(() => {
+          // 取消删除
+        });
+    },
+    //获取任务列表
+    getTaskList() {
+      let params = {
+        userId: this.userId
+      };
+      this.ajax
+        .get(this.$store.state.api + "select_classroomTask", params)
+        .then(res => {
+          let _data = res.data[0];
+          if (_data.length > 0) {
+            _data.forEach(i => (i.jsonData = JSON.parse(i.jsonData)));
+            this.dataList = _data;
+          }
+        });
+    },
+    updateTask(id) {
+      return new Promise((resolve, reject) => {
+        let _data = this.dataList.find(i => i.id === id);
+        if (_data) {
+          let params = [
+            {
+              id: _data.id,
+              userId: this.userId,
+              remarks: _data.remarks,
+              status: _data.status,
+              json: JSON.stringify(_data.jsonData),
+              createId: _data.createId,
+              batch: _data.batch
+            }
+          ];
+
+          this.ajax
+            .post(this.$store.state.api + "update_classroomTask", params)
+            .then(res => {
+              if (res.data == 1) {
+                console.log("修改任务成功");
+                resolve();
+              } else {
+                reject();
+                this.$message.error("修改任务失败");
+              }
+            })
+            .catch(err => {
+              console.log(err);
+              this.$message.error("修改任务失败");
+              reject();
+            });
+        }
+      });
+    },
+    changeData({ field, data }) {
+      let _index = this.dataList.findIndex(i => i.id === data.id);
+      if (_index != -1) {
+        if (this.dataList[_index][field] != data[field]) {
+          this.dataList[_index][field] = data[field];
+          this.updateTask(this.dataList[_index].id);
+        }
+      }
+    }
+  },
+  mounted() {
+    this.getTaskList();
+  }
+};
+</script>
+
+<style scoped>
+.batchCreationClassDialog {
+  width: 100vw;
+  height: 100vh;
+}
+
+.batchCreationClassDialog >>> .el-dialog__header,
+.batchCreationClassDialog >>> .el-dialog__footer {
+  display: none; /* 隐藏头部和底部 */
+}
+
+.batchCreationClassDialog >>> .el-dialog__body {
+  padding: 0;
+  margin: 0;
+  height: 100vh;
+  width: 100vw;
+  overflow: auto;
+  display: flex;
+  justify-content: center;
+}
+
+.box {
+  width: 70%;
+  min-width: 500px;
+  height: 100%;
+  box-sizing: border-box;
+  border-left: solid 1px #e1e1e1;
+  border-right: solid 1px #e1e1e1;
+  padding: 0 40px;
+}
+
+.b_back {
+  height: 50px;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-top: 20px;
+}
+
+.b_b_left {
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+}
+
+.b_b_left > img {
+  width: 25px;
+  height: 25px;
+  transform: rotate(180deg);
+}
+
+.b_b_left > span {
+  font-size: 16px;
+  font-weight: bold;
+  margin-left: 5px;
+}
+
+.b_head {
+  width: 100%;
+  height: 50px;
+  margin: 20px 0 10px 0;
+  box-sizing: border-box;
+}
+
+.b_head > span {
+  font-size: 18px;
+  font-weight: bold;
+  color: #000;
+}
+
+.b_nav {
+  width: 100%;
+  height: 50px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  box-sizing: border-box;
+  border-bottom: solid 1px rgba(150, 155, 163, 1);
+}
+
+.b_n_left {
+  width: calc(100% - 60px);
+  height: 100%;
+  display: flex;
+  align-items: center;
+}
+
+.b_n_left > span {
+  height: 100%;
+  padding: 0 20px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  position: relative;
+  cursor: pointer;
+  transition: 0.3s;
+  font-size: 16px;
+}
+
+.b_n_left > span:hover {
+  color: rgba(5, 96, 253, 1);
+}
+
+.b_n_left > .b_n_l_active {
+  color: rgba(5, 96, 253, 1);
+}
+
+.b_n_left > .b_n_l_active::after {
+  content: "";
+  position: absolute;
+  width: 70%;
+  height: 4px;
+  border-radius: 4px;
+  bottom: -1px;
+  left: 50%;
+  transform: translateX(-50%);
+  background-color: rgba(5, 96, 253, 1);
+}
+
+.b_n_right {
+  width: 60px;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  color: rgba(150, 155, 163, 1);
+  font-size: 16px;
+}
+
+.b_bottom {
+  width: 100%;
+  height: auto;
+  min-height: calc(100% - 210px);
+  /* height: calc(100% - 200px); */
+}
+
+.b_b_operation {
+  width: 100%;
+  height: 40px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-top: 10px;
+}
+.b_b_o_left {
+  width: calc(100% - 100px);
+  height: 100%;
+  display: flex;
+  align-items: center;
+  font-size: 16px;
+}
+
+.b_b_o_right {
+  width: 100px;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.b_b_o_right > div {
+  margin-right: 10px;
+  font-size: 16px;
+}
+
+.b_b_o_right > div >>> .el-checkbox {
+  display: flex;
+  align-items: center;
+}
+
+.b_b_o_right > span {
+  width: 20px;
+  height: 20px;
+  cursor: pointer;
+}
+
+.b_b_o_right > span > img {
+  width: 100%;
+  height: 100%;
+}
+
+.b_b_list {
+  width: 100%;
+  height: auto;
+  padding-bottom: 40px;
+}
+</style>

+ 956 - 0
src/components/pages/classroomObservation/dialog/editBaseMessageDialog.vue

@@ -0,0 +1,956 @@
+<template>
+	<div>
+		<el-dialog
+			:center="true"
+			:visible.sync="show"
+			:close-on-click-modal="false"
+      :modal="true"
+			width="auto"
+			height="auto"
+      :append-to-body="true"
+			class="editBaseMessageDialog"
+		>
+    <div class="box">
+      <div class="b_head">
+        <span>编辑基本信息</span>
+        <img src="../../../../assets/icon/classroomObservation/close.svg" @click="close()">
+      </div>
+      <div class="b_main">
+        <div class="m-m-form">
+				<div class="m-m-formItem" style="width: clamp(50%, 50%, 50%)">
+					<div class="m-m-fi-label">课堂名称</div>
+					<div class="m-m-fi-input">
+						<el-input
+							v-model="data.courseName"
+							placeholder="请输入课堂名称"
+							@change="changeData()"
+						></el-input>
+					</div>
+				</div>
+
+				<div class="m-m-formItem" style="width: clamp(21%,21%,21%)">
+					<div class="m-m-fi-label">授课老师</div>
+					<div class="m-m-fi-input">
+						<el-input
+							v-model="data.teacherName"
+							placeholder="请输入授课老师"
+							@change="changeData()"
+						></el-input>
+					</div>
+				</div>
+
+				<div class="m-m-formItem" style="width: clamp(22%,22%,22%)"">
+					<div class="m-m-fi-label">授课时间</div>
+					<div class="m-m-fi-input" style="position: relative;">
+						<el-date-picker
+							v-model="data.time"
+							type="datetime"
+							format="yyyy-MM-dd HH:mm:ss"
+							value-format="yyyy-MM-dd HH:mm:ss"
+							style="width: 100%;"
+							class="m_m_fi_inputDisabledIcon"
+							prefix-icon="none"
+							@change="changeData()"
+							placeholder="请选择授课时间"
+						>
+
+						</el-date-picker>
+						<i class="el-icon-date" style="position: absolute; right: 10px; top: 50%; transform: translateY(-50%); pointer-events: none;"></i>
+					</div>
+				</div>
+
+				<div class="m-m-formItem" style="width: clamp(24%,24%,24%)"">
+					<div class="m-m-fi-label">授课年级</div>
+					<div class="m-m-fi-input">
+						<el-select
+							style="width: 100%"
+							v-model="data.grade"
+							@change="changeData()"
+							placeholder="请选择年级"
+						>
+							<el-option
+								v-for="(item, index) in gradeList"
+								:key="index"
+								:value="item.value"
+								:label="item.label"
+							></el-option>
+						</el-select>
+					</div>
+				</div>
+
+				<div class="m-m-formItem" style="width: clamp(24.5%,24.5%,24.5%)">
+					<div class="m-m-fi-label">授课科目</div>
+					<div class="m-m-fi-input">
+						<el-select
+							style="width: 100%"
+							v-model="data.subject"
+							@change="changeData()"
+							placeholder="请选择科目"
+						>
+							<el-option
+								v-for="(item, index) in subjectList"
+								:key="index"
+								:value="item.value"
+								:label="item.label"
+							></el-option>
+						</el-select>
+					</div>
+				</div>
+
+				<div class="m-m-formItem" style="width: clamp(21%,21%,21%)"">
+					<div class="m-m-fi-label">教材版本</div>
+					<div class="m-m-fi-input">
+						<el-input
+							v-model="data.textbook"
+							placeholder="请输入教材版本"
+							@change="changeData()"
+						></el-input>
+					</div>
+				</div>
+
+				<div class="m-m-formItem" style="width: clamp(22%,22%,22%)"">
+					<div class="m-m-fi-label">学生人数</div>
+					<div class="m-m-fi-input">
+						<el-input
+							v-model.number="data.studentNum"
+							placeholder="请输入学生人数"
+							@change="changeData()"
+						></el-input>
+					</div>
+				</div>
+			</div>
+			<div class="fileList">
+				<div class="fl_img">
+					<div class="imgTit">
+						<span>课堂图片</span>
+						<span>(建议图片比例16:9,最多上传3张)</span>
+					</div>
+					<div class="m-m-formImage" v-loading="uploadImageLoading">
+								<div
+									class="m-m-fi-imageItem"
+									v-for="(value, key, index) in data.imageList"
+									:key="index"
+									v-if="(key == 'fileList1' || key == 'fileList2' || key == 'fileList3') && value.length >0"
+									@click.stop="previewImg(value[0].url)"
+									style="max-width:32%;"
+								>
+									<el-image
+										class="itemUrl"
+										:src="value[0].url"
+										fit="cover"
+										style="width: 100%;"
+									></el-image>
+									<span @click.stop="delImage(key)"></span>
+									<!-- <img class="itemUrl" :src="value[0].url" alt="" /> -->
+								</div>
+								<!-- 图片区域 -->
+
+							<div
+								class="m-m-fi-imageItem"
+								@click.stop="addImage2()"
+								style="max-width:32%;"
+								v-if="
+									data.imageList.fileList1 &&
+									data.imageList.fileList1.length +
+                  data.imageList.fileList2.length +
+                  data.imageList.fileList3.length <
+										3
+								"
+							>
+								<img
+									src="../../../../assets/icon/classroomObservation/Union.svg"
+									alt=""
+								/>
+								<div
+									style="
+										font-size: 12px;
+										font-weight: 400;
+										margin-top: 5px;
+										color: rgba(0, 0, 0, 0.4);
+									"
+								>
+									点击上传图片
+								</div>
+							</div>
+							<!-- 图片区域 -->
+						<!-- <div class="m-m-fi-btn" >添加课堂图片</div> -->
+					</div>
+				</div>
+				<div class="fl_video">
+					<div class="imgTit">
+						<span>课堂视频</span>
+						<span></span>
+						<!-- <span>(建议视频比例16:9,最多上传1个)</span> -->
+					</div>
+
+					<div class="m-m-formImage" v-loading="uploadVideoLoading">
+
+								<div
+									class="m-m-fi-imageItem"
+									style="max-width:100%;"
+									v-if="!data.imageList.videoList.length==0"
+									v-for="(item, index) in data.imageList.videoList?data.imageList.videoList:[]"
+									:key="index"
+									@click.stop="previewVideo(item.url)"
+								>
+								<!-- <div>{{ item }}</div> -->
+									<el-image
+										class="itemUrl"
+										:src="require('../../../../assets/icon/classroomObservation/isVideo.png')"
+										fit="cover"
+									></el-image>
+									<span @click.stop="delVideo('videoList')"></span>
+								</div>
+							<!-- <el-progress v-if="progressData.uploadVideo && !imageList.videoList.length" class="m_m_fi_progress" :percentage="progressData.value"></el-progress> -->
+							<el-popover
+  					  placement="top"
+  					  width="180"
+  					  trigger="hover"
+							style="width: 140px"
+							v-if="(((data.imageList.videoList&&data.imageList.videoList.length<=0) || !data.imageList.videoList) && !progressData.uploadVideo)"
+  					>
+						<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="resourceUploadVideo()">资源库上传</el-button>
+							</el-button-group>
+						</div>
+							<div
+							  slot="reference"
+								class="m-m-fi-imageItem"
+								@click.stop="addVideo()"
+								style="max-width:100%;"
+								v-if="(((data.imageList.videoList&&data.imageList.videoList.length<=0) || !data.imageList.videoList) && !progressData.uploadVideo)"
+							>
+								<img
+									src="../../../../assets/icon/classroomObservation/Union.svg"
+									alt=""
+								/>
+								<div
+									style="
+										font-size: 12px;
+										font-weight: 400;
+										margin-top: 5px;
+										color: rgba(0, 0, 0, 0.4);
+									"
+								>
+									点击上传视频
+								</div>
+							</div>
+						</el-popover>
+
+							<div
+								class="m-m-fi-imageItem"
+								style="max-width:60%;border-radius: 8px;overflow: hidden;"
+								v-if="progressData.uploadVideo && !data.imageList.videoList.length""
+							>
+							<el-image
+								class="itemUrl"
+								:src="require('../../../../assets/icon/classroomObservation/videoFile.svg')"
+								fit="cover"
+							></el-image>
+							<div class="m_m_fi_videBtn">
+								<img v-if="!progressData.stop" :src="require('../../../../assets/icon/classroomObservation/stopIcon.png')" @click.stop="stopUploadVideo()">
+								<img v-if="progressData.stop" :src="require('../../../../assets/icon/classroomObservation/startIcon.png')"  @click.stop="startUploadVideo()">
+								<img :src="require('../../../../assets/icon/classroomObservation/delFile.svg')" @click.stop="delUploadVideo()">
+							</div>
+							<div class="m_m_fi_progress">
+									<div>{{ progressData.value }}%</div>
+									<span v-if="!progressData.stop">上传中...</span>
+									<span v-else>已暂停</span>
+									<div class="m_m_fi_p_bar">
+										<div :style="{width:progressData.value+'%'}"></div>
+									</div>
+							</div>
+						</div>
+						<!-- <div class="m-m-fi-btn" >添加课堂图片</div> -->
+					</div>
+				</div>
+				<!-- <div class="nephogramArea"> -->
+				<div class="fl_nephogram">
+					<div class="imgTit">
+						<span>词云图</span>
+						<span></span>
+					</div>
+					<div class="m-m-formImage">
+								<div
+									class="m-m-fi-nephogramItem"
+									style="width:100%;"
+									 v-loading="uploadNephogramLoading"
+									v-if="(data.imageList.NephogramList&&data.imageList.NephogramList.length>0)"
+								>
+									<wordcloudEChart :data="imageList.NephogramList[0]"/>
+									<span @click.stop="delNephogram('NephogramList')"></span>
+								</div>
+							<div
+								class="m-m-fi-nephogramItem"
+								 v-loading="uploadNephogramLoading"
+								@click.stop="addNephogram()"
+								style="width:60%;max-height: 100px;"
+								v-if="((data.imageList.NephogramList&&data.imageList.NephogramList.length<=0) || !data.imageList.NephogramList)"
+							>
+								<img
+									:src="require('../../../../assets/icon/classroomObservation/bmRefresh.png')"
+									alt=""
+									style="width:20px"
+								/>
+								<div
+									style="
+										font-size: 12px;
+										font-weight: 400;
+										margin-top: 5px;
+										color: rgba(0, 0, 0, 0.4);
+									"
+								>
+									生成词云图
+								</div>
+							</div>
+					</div>
+				<!-- </div> -->
+			</div>
+
+			</div>
+      </div>
+      <div class="b_bottom">
+        <el-button size="small" @click="close()">取消</el-button>
+        <el-button size="small" type="primary" @click="submit">确定修改</el-button>
+      </div>
+      <previewVideoDialog ref="previewVideoDialogRef" v-if="show"/>
+		<uploadFile v-if="progressData.uploadVideo && show" ref="uploadFileRef" @progressUpdate="videoProgressUpdate" @delUpload="videoDelUpload" @success="updateVideoSuccess" @startUpload="videoStartUpload"/>
+		<resourceLibraryDialog v-if="show" ref="resourceLibraryDialogRef" @addFile="resourceLibraryDialogAddFile"/>
+    </div>
+		</el-dialog>
+
+	</div>
+</template>
+
+<script>
+import previewVideoDialog from '../components/previewVideoDialog.vue';
+import wordcloudEChart from '../components/wordcloudEChart.vue'
+import uploadFile from '../components/uploadFile.vue';
+import resourceLibraryDialog from '../components/resourceLibraryDialog.vue';
+export default {
+  components:{
+    previewVideoDialog,
+    wordcloudEChart,
+    uploadFile,
+    resourceLibraryDialog
+  },
+	data() {
+		return {
+			loading: false,
+			show: false,
+      uploadVideoLoading:false,
+      uploadImageLoading:false,
+      uploadNephogramLoading:false,
+      editId:null,
+      data: {
+				courseName: "",
+				teacherName: "",
+        time:"",
+				grade: "",
+				subject: "",
+        textbook:"",
+				studentNum: 0,
+        imageList:{
+        fileList1: [],
+        fileList2: [],
+        fileList3: [],
+        NephogramList: [],
+        videoList: []
+      },
+			},
+      gradeList: [
+				{ value: "小学一年级", label: "小学一年级" },
+				{ value: "小学二年级", label: "小学二年级" },
+				{ value: "小学三年级", label: "小学三年级" },
+				{ value: "小学四年级", label: "小学四年级" },
+				{ value: "小学五年级", label: "小学五年级" },
+				{ value: "小学六年级", label: "小学六年级" },
+				{ value: "初中一年级", label: "初中一年级" },
+				{ value: "初中二年级", label: "初中二年级" },
+				{ value: "初中三年级", label: "初中三年级" },
+				{ value: "高中一年级", label: "高中一年级" },
+				{ value: "高中二年级", label: "高中二年级" },
+				{ value: "高中三年级", label: "高中三年级" },
+			],
+			subjectList: [
+				{ value: "语文", label: "语文" },
+				{ value: "数学", label: "数学" },
+				{ value: "英语", label: "英语" },
+				{ value: "科学", label: "科学" },
+				{ value: "信息技术", label: "信息技术" },
+				{ value: "心理", label: "心理" },
+				{ value: "物理", label: "物理" },
+				{ value: "化学", label: "化学" },
+				{ value: "生物", label: "生物" },
+				{ value: "历史", label: "历史" },
+				{ value: "地理", label: "地理" },
+				{ value: "通用技术", label: "通用技术" },
+				{ value: "政治", label: "政治" },
+				{ value: "STEM", label: "STEM" },
+				{ value: "美术", label: "美术" },
+				{ value: "音乐", label: "音乐" },
+				{ value: "其他", label: "其他" },
+			],
+
+			progressData:{
+				stop:false,
+				uploadVideo:false,
+				value:0,
+				status:"",
+				key:"",
+				uploadid:"",
+			},
+			uploadFileObj:null,
+			loading:false,
+		};
+	},
+	methods: {
+		open({editId,message}) {
+      this.editId = editId;
+      this.data = JSON.parse(JSON.stringify(message));
+      this.loading = false;
+			this.show = true;
+		},
+		close() {
+			this.show = false;
+			this.init();
+		},
+		init() {
+			this.loading = false;
+      this.data = {
+        courseName: "",
+				teacherName: "",
+        time:"",
+				grade: "",
+				subject: "",
+        textbook:"",
+				studentNum: 0,
+        imageList:{
+        fileList1: [],
+        fileList2: [],
+        fileList3: [],
+        NephogramList: [],
+        videoList: []
+      }
+    }
+    this.progressData = {
+      stop:false,
+				uploadVideo:false,
+				value:0,
+				status:"",
+				key:"",
+				uploadid:"",
+    }
+      this.editId = null;
+		},
+    submit(){
+      if(!this.data.courseName.trim())return this.$message.error("请输入胡课堂名称");
+      this.$emit("success",{editId:this.editId,message:JSON.parse(JSON.stringify(this.data))})
+    },
+    changeData(){
+      // this.$message.info("更新数据")
+    },
+    //本地上传
+    localUploadVideo(){
+      this.$message.info("本地上传")
+    },
+    //资源库上传
+    resourceUploadVideo(){
+      this.$message.info("资源库上传")
+    },
+    //添加视频
+    addVideo(){
+      this.$message.info("添加视频")
+    },
+    //删除词云图
+    delNephogram(){
+      this.$message.info("删除词云图")
+    },
+    //生成词云图
+    addNephogram(){
+      this.$message.info("生成词云图")
+    },
+    //资源库添加文件
+    resourceLibraryDialogAddFile(file){
+      let _file = file[0];
+			if(!_file.file.endsWith(".mp4")){
+				this.$message.error("请选择mp4视频文件")
+				return;
+			}
+      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});
+			// })
+    },
+    videoProgressUpdate(data){
+			if(data.status=="processing"){
+				this.progressData.value = data.percent;
+				this.progressData.status = data.status;
+			}else if(data.status=="fail"){
+				this.progressData.value = data.percent;
+				this.progressData.status = data.status;
+				this.$message.error('上传发生错误,请点击继续上传')
+				this.$refs.uploadFileRef.stopUpload();
+			}else if(data.status=="success"){
+				this.progressData.value = data.percent;
+				this.progressData.status = data.status;
+				this.$refs.uploadFileRef.stopUpload();
+			}else if(data.status=="error"){
+				this.progressData.stop = true;
+				this.progressData.status = data.status;
+				this.$refs.uploadFileRef.stopUpload();
+				this.$message.error('上传发生错误,请重新上传')
+			}
+			console.log(this.progressData)
+		},
+		videoDelUpload(res){
+			this.progressData.uploadVideo = false;
+			this.progressData.uploadid = "";
+			this.progressData.key = "";
+			this.progressData.status = "";
+			this.progressData.stop = false;
+			this.progressData.value = 0;
+			this.$message.success("已删除视频");
+		},
+    videoStartUpload({key,uploadid}){
+			this.progressData.uploadid = uploadid;
+			this.progressData.key = key;
+			this.progressData.status = "start"
+		},
+		updateVideoSuccess(res){
+			if(!res.data)return;
+			this.progressData.uploadVideo = false;
+			this.progressData.stop = false;
+			this.progressData.uploadid = "";
+			this.progressData.status = "";
+			this.progressData.value = 0;
+			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;
+			// })
+		},
+    //上传课堂图片
+    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;
+      //   })
+			// };
+    },
+    previewVideo(url){
+      this.$refs.previewVideoDialogRef.open(url)
+    },
+	},
+};
+</script>
+
+<style scoped>
+.editBaseMessageDialog >>> .el-dialog {
+  width: 1200px !important;
+	border-radius: 8px;
+	background-color: #fff;
+	overflow: hidden;
+  padding: 0;
+}
+
+.editBaseMessageDialog >>> .el-dialog__body {
+  width: 1200px !important;
+	height: auto;
+	flex-shrink: 0;
+	box-sizing: border-box;
+	overflow: auto;
+  padding: 0;
+}
+
+.editBaseMessageDialog >>> .el-dialog__header {
+	display: none !important;
+}
+
+.box{
+  width:1200px;
+  height:500px;
+  background:#FAFAFA;
+  border-radius:15px;
+  box-shadow: 0px 6px 30px 5px rgba(0, 0, 0, 0.05), 0px 16px 24px 2px rgba(0, 0, 0, 0.04), 0px 8px 10px -5px rgba(0, 0, 0, 0.08);
+}
+
+.b_head{
+  width:100%;
+  height:50px;
+  border-radius:15px 15px 0 0;
+  background:#fff;
+  display:flex;
+  align-items:center;
+  justify-content:space-between;
+  box-sizing: border-box;
+  padding: 0 20px;
+}
+
+.b_head>span{
+  font-size: 18px;
+  font-weight: bold;
+  color: #000;
+}
+
+.b_head>img{
+  width: 20px;
+  height: 20px;
+  cursor: pointer;
+}
+
+.b_main{
+  width:100%;
+  height:calc(100% - 50px - 70px);
+  background:#FAFAFA;
+	padding: 20px 20px 0 20px;
+	box-sizing: border-box;
+	display: flex;
+	flex-wrap: wrap;
+	flex-direction: column;
+}
+
+.m-m-form {
+	display: flex;
+	flex-wrap: wrap;
+	width: 100%;
+
+}
+
+.m-m-formItem {
+	width: 100%;
+	height: auto;
+	display: flex;
+	flex-direction: column;
+	align-items: left;
+	margin-top: 10px;
+	margin-right: 1.5%;
+	/* margin-bottom: 10px; */
+}
+
+.m-m-fi-input {
+	max-width: 100%;
+}
+
+.m_m_fi_inputDisabledIcon>>> .el-input__prefix{
+	display: none;
+}
+
+.m_m_fi_inputDisabledIcon>>>.el-input__inner{
+	padding-left: 15px;
+}
+
+.m-m-fi-label {
+	font-size: 16px;
+	display: flex;
+	padding-bottom: 5px;
+	/* justify-content: center; */
+	/* align-items: center; */
+	box-sizing: border-box;
+	/* padding: 0 10px; */
+	text-wrap: nowrap;
+	min-width: fit-content;
+}
+
+.m-m-formImage {
+	width: 100%;
+	height: auto;
+	margin-bottom: 20px;
+	display: flex;
+	justify-content: flex-start;
+	/* flex-wrap: wrap; */
+}
+
+.m-m-fi-imageList {
+	width: 100%;
+	height: auto;
+	display: flex;
+	flex-wrap: wrap;
+}
+
+.m-m-fi-imageItem {
+	width: auto;
+	height: auto;
+	background-color: rgba(238, 238, 238, 1);
+	width: 140px;
+	height: 100px;
+	min-width: 110px !important;
+	margin-right: 1%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	flex-direction: column;
+	box-sizing: border-box;
+	font-size: 14px;
+	cursor: pointer;
+	position: relative;
+	margin-bottom: 10px;
+	border-radius: 4px;
+	border: dashed 1px #DCDCDC;
+	overflow: hidden;
+}
+.m-m-fi-imageItem > .itemUrl {
+	width: 100%;
+	height: 100%;
+}
+
+.m-m-fi-imageItem > span {
+	width: 20px;
+	height: 20px;
+	position: absolute;
+	right: 4px;
+	top: 4px;
+	background-image: url("../../../../assets/icon/classroomObservation/delFile.svg");
+	background-repeat: no-repeat;
+	background-size: 100% 100%;
+	display: none;
+	z-index: 9999;
+	/* display: flex;
+	justify-content: flex-end;
+	align-items: flex-start;
+	box-sizing: border-box;
+	padding: 2px 10px;
+	color: #666666;
+	font-size: 18px;
+	background-color: #FFBBBB;
+	cursor: pointer;
+	border-radius: 100%;
+	display: none; */
+}
+
+.m-m-fi-imageItem:hover > span {
+	display: flex;
+}
+
+/* .m-m-fi-i-icon {
+	width: 20px;
+	height: 20px;
+	background: url("../../../../assets/icon/classroomObservation/file.png")
+		no-repeat;
+	background-size: 100% 100%;
+	margin-right: 5px;
+} */
+
+.m-m-fi-btn {
+	width: auto;
+	height: 35px;
+	box-sizing: border-box;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	border-radius: 18px;
+	font-size: 14px;
+	border: solid 1px #c5c5c5;
+	background-color: white;
+	cursor: pointer;
+	padding: 0 10px;
+}
+
+.fileList{
+	width: 100%;
+	position: relative;
+	display: flex;
+}
+
+.fl_img{
+	width: 50%;
+	height: auto;
+}
+
+.fl_video{
+	width: 21.8%;
+	margin-left: 1.6%;
+	height: auto;
+
+}
+
+.nephogramArea{
+	width: 20%;
+	height: auto;
+}
+
+.fl_nephogram{
+	width: 20%;
+	/* margin-left: 1.5%; */
+	height: auto;
+	margin-left: 1%;
+	/* margin-top: -2%; */
+}
+
+
+.m_m_fi_progress{
+	width: 100%;
+	height: 100%;
+	position: absolute;
+	display: flex;
+	flex-direction: column;
+	justify-content: center;
+	align-items: center;
+	/* 加载 */
+	/* cursor:wait !important; */
+	background-color: #00000099;
+}
+
+.m_m_fi_progress>div:nth-child(1){
+	font-size: 20px;
+	color: white;
+}
+
+.m_m_fi_progress>span:nth-child(2){
+	font-size: 16px;
+	color: #FFFFFF8C;
+}
+
+.m_m_fi_p_bar{
+	width: 100%;
+	height: 5px;
+	position: absolute;
+	bottom: 0;
+	background-color: #D8E3F7;
+}
+
+.m_m_fi_p_bar>div{
+	background-color: #3975CE;
+	max-width: 100%;
+	height: 100%;
+}
+
+.m-m-fi-nephogramItem{
+	width: 140px;
+	height: 100px;
+	background-color: rgba(238, 238, 238, 1);
+	min-width: 140px;
+	min-height: 100px;
+	margin-right: 1%;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+	flex-direction: column;
+	box-sizing: border-box;
+	font-size: 14px;
+	cursor: pointer;
+	position: relative;
+	margin-bottom: 10px;
+	border-radius: 4px;
+	border: dashed 1px #DCDCDC;
+	overflow: hidden;
+
+}
+.m-m-fi-nephogramItem> .itemUrl {
+	width: 100%;
+	height: 100%;
+}
+
+.m-m-fi-nephogramItem > span {
+	width: 20px;
+	height: 20px;
+	position: absolute;
+	right: 4px;
+	top: 4px;
+	background-image: url("../../../../assets/icon/classroomObservation/delFile.svg");
+	background-repeat: no-repeat;
+	background-size: 100% 100%;
+	display: none;
+	z-index: 99999;
+	/* display: flex;
+	justify-content: flex-end;
+	align-items: flex-start;
+	box-sizing: border-box;
+	padding: 2px 10px;
+	color: #666666;
+	font-size: 18px;
+	background-color: #FFBBBB;
+	cursor: pointer;
+	border-radius: 100%;
+	display: none; */
+}
+
+.m-m-fi-nephogramItem:hover > span {
+	display: flex;
+}
+
+.m_m_fi_videBtn{
+	position: absolute;
+	right: 4px;
+	top: 4px;
+	width:auto;
+	height: 20px;
+	background-size: 100% 100%;
+	/* display: none; */
+	z-index: 99999;
+	display: flex;
+}
+
+.m_m_fi_videBtn>img{
+	width: 20px;
+	height: 20px;
+	margin-left:10px;
+	cursor: pointer;
+}
+
+.imgTit {
+	height: 40px;
+	line-height: 40px;
+}
+.imgTit :first-child {
+	font-size: 16px;
+	font-weight: 400;
+	line-height: 22px;
+	text-align: right;
+}
+.imgTit :last-child {
+	font-family: PingFang SC;
+	font-size: 12px;
+	font-weight: 400;
+	line-height: 20px;
+	text-align: left;
+	color: rgba(0, 0, 0, 0.4);
+}
+
+.b_bottom{
+  width: 100%;
+  height: 70px;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  box-sizing: border-box;
+  padding: 0 20px;
+}
+</style>

+ 547 - 0
src/components/pages/classroomObservation/dialog/uploadFileToCreateClassDialog.vue

@@ -0,0 +1,547 @@
+<template>
+  <div>
+    <el-dialog
+      :center="true"
+      :visible.sync="show"
+      :close-on-click-modal="false"
+      :modal="true"
+      width="auto"
+      height="auto"
+      :append-to-body="true"
+      class="uploadFileToCreateClassDialog"
+    >
+      <div class="box">
+        <div class="b_head">
+          <span>文件上传与配置</span>
+          <img
+            src="../../../../assets/icon/classroomObservation/close.svg"
+            @click="close()"
+          />
+        </div>
+        <div class="b_main">
+          <div class="b_m_left">
+            <div class="b_m_l_fileList" v-if="fileList.length > 0">
+              <div class="fl_item" v-for="item in fileList" :key="item.index">
+                <div class="fl_i_left">
+                  <img
+                   v-if="item.type =='text/plain'"
+                    src="../../../../assets/icon/classroomObservation/textFile_icon.svg"
+                  />
+
+                  <img
+                   v-if="item.type =='audio/wav'"
+                    src="../../../../assets/icon/classroomObservation/audio_file.svg"
+                  />
+
+                  <img
+                   v-if="item.type =='video/mp4'"
+                    src="../../../../assets/icon/classroomObservation/videoFile_icon.svg"
+                  />
+                </div>
+                <div class="fl_i_center">
+                  <div class="fl_i_c_top">
+                    <span>{{ item.name }}</span>
+                    <div>
+                      <span v-if="item.status === 'wait'">等待上传</span>
+                      <span
+                        class="uploadingText"
+                        v-else-if="item.status === 'uploading'"
+                        >{{ item.progress.percent }}%</span
+                      >
+                      <span
+                        class="successText"
+                        v-else-if="item.status === 'success'"
+                        >上传成功
+                      </span>
+                    </div>
+                  </div>
+                  <div class="fl_i_c_bottom">
+                    <div class="f_i_c_progress">
+                      <div
+                        class="f_i_c_p_value"
+                        :class="{
+                          successProgress: item.status === 'success',
+                          uploadingProgress: item.status === 'uploading'
+                        }"
+                        :style="`width:${item.progress.percent}%`"
+                      ></div>
+                    </div>
+                  </div>
+                </div>
+                <div class="fl_i_right">
+                  <img
+                    @click="delUploadFile(item)"
+                    src="../../../../assets/icon/classroomObservation/del.svg"
+                  />
+                </div>
+              </div>
+            </div>
+            <div class="b_m_l_noFile" v-else @click="addFile()">
+              <img
+                src="../../../../assets/icon/classroomObservation/file_processing.svg"
+              />
+              <span>文件格式支持:mp4、wav、txt 文件</span>
+            </div>
+          </div>
+          <div class="b_m_right">
+            <span>配置</span>
+            <div>
+              <span>自动编码</span>
+              <el-switch
+                v-model="automaticCoding"
+                active-color="#13ce66"
+                inactive-color="#ff4949"
+              >
+              </el-switch>
+            </div>
+            <div>
+              <span>分析模板</span>
+              <el-cascader
+                size="small"
+                v-model="analysisTemplate"
+                :options="options"
+              ></el-cascader>
+            </div>
+          </div>
+        </div>
+        <div class="b_bottom">
+          <el-button size="small" @click="close()">取消</el-button>
+          <el-button size="small" type="primary" @click="submit"
+            >确定添加</el-button
+          >
+        </div>
+        <uploadFile
+          v-for="(item, index) in fileList"
+          v-if="item.status == 'uploading'"
+          :ref="`uploadFileRef_${item.index}`"
+          :key="item.index"
+          :index="item.index"
+          @progressUpdate="progressUpdate"
+          @success="uploadSuccess"
+        />
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import uploadFile from "../newComponents/uploadFile.vue";
+import { v4 as uuidv4 } from "uuid";
+export default {
+  components: {
+    uploadFile
+  },
+  data() {
+    return {
+      loading: false,
+      show: false,
+      automaticCoding: false,
+      analysisTemplate: [],
+      userId: this.$route.query["userid"],
+      fileList: [],
+      options: [
+        { value: "0", label: "社区", children: [] },
+        { value: "1", label: "我的", children: [] },
+        { value: "2", label: "收藏", children: [] }
+      ]
+    };
+  },
+  methods: {
+    open(data) {
+      this.init();
+      this.getTemplateData();
+      this.loading = false;
+      this.show = true;
+    },
+    close() {
+      this.show = false;
+      this.init();
+    },
+    init() {
+      this.fileList = [];
+      this.analysisTemplate = [];
+      this.automaticCoding = false
+      this.loading = false;
+    },
+    submit() {
+      if(this.fileList.length<=0)return this.$message.error("请上传文件");
+      if(this.analysisTemplate.length<=1)return this.$message.error("请选择分析模板");
+      if(this.fileList.some(i=>i.status==='wait' || i.status==='uploading'))return this.$message.error("请等待文件上传完毕");
+      this.$emit('success',{fileList:this.fileList,automaticCoding:this.automaticCoding,analysisTemplate:this.analysisTemplate});
+    },
+    delUploadFile(item) {
+      this.$confirm("确定要删除吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning"
+      })
+        .then(() => {
+          // 确定删除
+
+          if (item.status === "uploading") {
+            this.$refs[`uploadFileRef_${item.index}`][0].stopUpload();
+            this.$refs[`uploadFileRef_${item.index}`][0].abortMultipartUpload(
+              item.progress.key,
+              item.progress.uploadid
+            );
+          }
+          this.fileList = this.fileList.filter(i => i.index != item.index);
+          this.$forceUpdate();
+        })
+        .catch(() => {
+          // 取消删除
+        });
+    },
+    progressUpdate(res) {
+      console.log(res);
+      this.fileList.find(i => i.index == res.index).progress.status =
+        res.status;
+      this.fileList.find(i => i.index == res.index).progress.percent =
+        res.percent;
+      this.fileList.find(i => i.index == res.index).progress.key = res.key;
+      this.fileList.find(i => i.index == res.index).progress.uploadid =
+        res.uploadid;
+    },
+    uploadSuccess(res) {
+      let data = res.data;
+      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
+      };
+      this.fileList.find(i => i.index == res.index).status = "success";
+      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
+          });
+        });
+      } else {
+        console.log("上传完成");
+      }
+    },
+    addFile() {
+      let input = document.createElement("input");
+      input.type = "file";
+      input.accept = "video/mp4, audio/wav, text/plain";
+      input.multiple = true; // 支持多文件上传
+      input.style.display = "none";
+
+      input.click();
+
+      input.addEventListener("change", 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)
+          ) {
+            this.fileList.push({
+              file: files[i],
+              index: uuidv4(),
+              successData: null,
+              name: files[i].name,
+              type: files[i].type,
+              progress: { status: "", percent: 0, key: "", uploadid: "" },
+              status: "wait"
+            });
+          } else {
+            this.$message.info("文件格式不支持,仅支持mp4、wav、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
+              });
+            });
+          }
+        }
+      });
+    },
+    async getTemplateData() {
+      const promises = this.options.map(option => {
+        return new Promise((resolve, reject) => {
+          const params = {
+            uid: this.userId,
+            txt: "",
+            sub: "0",
+            type1: option.value
+          };
+
+          this.ajax
+            .get(this.$store.state.api + "selectClassroomTemplate", params)
+            .then(res => {
+              const _data = res.data[0].map(item => ({
+                value: item.id,
+                label: item.name
+              }));
+              this.options.find(
+                option2 => option2.value === option.value
+              ).children = _data;
+              resolve();
+            })
+            .catch(err => {
+              console.log(err);
+              reject(err);
+            });
+        });
+      });
+
+      try {
+        await Promise.all(promises);
+        console.log("获取模板成功");
+      } catch (error) {
+        console.log("获取模板失败");
+      }
+    }
+  }
+};
+</script>
+
+<style scoped>
+.uploadFileToCreateClassDialog >>> .el-dialog {
+  width: 900px !important;
+  border-radius: 8px;
+  padding: 0;
+  background-color: #fff;
+  overflow: hidden;
+}
+
+.uploadFileToCreateClassDialog >>> .el-dialog__body {
+  width: 900px !important;
+  height: auto;
+  flex-shrink: 0;
+  padding: 0;
+  box-sizing: border-box;
+  overflow: auto;
+}
+
+.uploadFileToCreateClassDialog >>> .el-dialog__header {
+  display: none !important;
+}
+
+.box {
+  width: 900px;
+  height: 500px;
+  background: #fafafa;
+  border-radius: 15px;
+  box-shadow: 0px 6px 30px 5px rgba(0, 0, 0, 0.05),
+    0px 16px 24px 2px rgba(0, 0, 0, 0.04), 0px 8px 10px -5px rgba(0, 0, 0, 0.08);
+}
+
+.b_head {
+  width: 100%;
+  height: 50px;
+  border-radius: 15px 15px 0 0;
+  background: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  box-sizing: border-box;
+  padding: 0 20px;
+}
+
+.b_head > span {
+  font-size: 18px;
+  font-weight: bold;
+  color: #000;
+}
+
+.b_head > img {
+  width: 20px;
+  height: 20px;
+  cursor: pointer;
+}
+
+.b_main {
+  width: 100%;
+  height: calc(100% - 50px - 70px);
+  background: #fafafa;
+  padding: 20px 20px 0 20px;
+  box-sizing: border-box;
+  display: flex;
+  align-items: center;
+  justify-content: space-around;
+}
+
+.b_bottom {
+  width: 100%;
+  height: 70px;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  box-sizing: border-box;
+  padding: 0 20px;
+}
+
+.b_main > div {
+  border-radius: 15px;
+  width: 400px;
+  height: 350px;
+  box-sizing: border-box;
+  border: rgba(150, 155, 163, 1) 1px dashed;
+}
+
+.b_m_left {
+  background-color: #fff;
+}
+
+.b_m_l_noFile {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  cursor: pointer;
+}
+
+.b_m_l_noFile > img {
+  width: 140px;
+  height: 140px;
+}
+
+.b_m_l_noFile > span {
+  font-size: 16px;
+  color: #000;
+}
+
+.b_m_right {
+  box-sizing: border-box;
+  padding: 20px;
+}
+
+.b_m_right > span {
+  font-size: 18px;
+  font-weight: bold;
+  color: #000;
+}
+
+.b_m_right > div {
+  margin-top: 20px;
+  font-size: 16px;
+  color: #000;
+}
+
+.b_m_l_fileList {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  overflow: auto;
+  box-sizing: border-box;
+  padding: 5px 10px;
+}
+
+.fl_item {
+  width: 100%;
+  height: 60px;
+  margin: 5px 0;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.fl_i_left {
+  width: 50px;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.fl_i_left > img {
+  width: 80%;
+  height: 80%;
+}
+
+.fl_i_center {
+  width: calc(100% - 90px);
+  height: 100%;
+}
+
+.fl_i_right {
+  width: 40px;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.fl_i_right > img {
+  width: 25px;
+  height: 25px;
+  cursor: pointer;
+}
+
+.fl_i_c_top {
+  width: 100%;
+  display: flex;
+  align-items: center;
+  height: 50%;
+  justify-content: space-between;
+}
+
+.fl_i_c_top > span {
+  max-width: calc(100% - 100px);
+  display: block;
+  align-items: center;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.fl_i_c_top > div {
+  width: 100px;
+  display: flex;
+  justify-content: flex-end;
+  font-size: 14px;
+}
+.fl_i_c_bottom {
+  width: 100%;
+  height: 50%;
+  display: flex;
+  align-items: center;
+}
+
+.f_i_c_progress {
+  width: 100%;
+  height: 6px;
+  background-color: rgba(217, 217, 217, 1);
+  border-radius: 5px;
+  overflow: hidden;
+}
+
+.f_i_c_p_value {
+  height: 100%;
+}
+
+.successText {
+  color: rgba(188, 230, 133, 1);
+}
+
+.successProgress {
+  background-color: rgba(188, 230, 133, 1);
+}
+
+.uploadingText {
+  color: rgba(54, 129, 252, 1);
+}
+
+.uploadingProgress {
+  background-color: rgba(54, 129, 252, 1);
+}
+</style>

+ 20 - 2
src/components/pages/classroomObservation/index.vue

@@ -82,6 +82,11 @@
 				</div> -->
       </div>
       <div class="co-h2-right">
+
+        <!--<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
           :class="['co-h2-r-btn', fileId && tid ? '' : 'ca-h2-r-noActive']"
           style="background: rgba(54, 129, 252, 1)"
@@ -201,6 +206,7 @@
       ref="changeCourseNameDialogRef"
       @success="changeCourseSuccess"
     />
+    <batchCreationClassDialog ref="batchCreationClassDialogRef"/>
 
     <!-- <addNewCourseDialog
 			:courseList="optionData"
@@ -231,12 +237,14 @@ import "echarts-wordcloud";
 import htmlDocx from "html-docx-js/dist/html-docx";
 import saveAs from "file-saver";
 import MarkdownIt from "markdown-it";
+import batchCreationClassDialog from './dialog/batchCreationClassDialog.vue'
 export default {
   components: {
     chatArea,
     messageArea,
     sharePdf,
-    changeCourseNameDialog
+    changeCourseNameDialog,
+    batchCreationClassDialog
     // addNewCourseDialog,
   },
   data() {
@@ -1202,7 +1210,10 @@ export default {
     },
 		getVideoAudioSuccess(file){
 			this.$refs.chatAreaRef.getVideoAudioSuccess(file)
-		}
+		},
+    batchBtn(){
+      this.$refs.batchCreationClassDialogRef.open();
+    },
   },
   mounted() {
     this.getCourseList().then(_ => {
@@ -1401,6 +1412,13 @@ export default {
   background-size: 100% 100%;
 }
 
+.co-h2-r-b-icon3 {
+  background: url("../../../assets/icon/classroomObservation/batch_icon.svg") no-repeat;
+  background-size: 100% 100%;
+}
+
+
+
 /* .co-h2-r-b-icon2 {
 	background: url("../../../assets/icon/classroomObservation/daoChu.png")
 		no-repeat;

+ 384 - 0
src/components/pages/classroomObservation/newComponents/batchClassCard.vue

@@ -0,0 +1,384 @@
+<template>
+  <div class="batchClassCard">
+    <div class="bcc_left">
+      <el-checkbox v-model="checked" @change="changeChecked"></el-checkbox>
+    </div>
+    <div class="bcc_right">
+      <div class="bcc_r_top">
+        <div class="bcc_r_t_left">
+          <div class="bcc_r_t_l_image">
+            <img
+              v-if="cardData.jsonData.fileData.type=='text/plain'"
+              src="../../../../assets/icon/classroomObservation/file_icon.svg"
+            />
+            <img
+              v-if="cardData.jsonData.fileData.type=='audio/wav'"
+              src="../../../../assets/icon/classroomObservation/audio_file.svg"
+            />
+            <img
+              v-if="cardData.jsonData.fileData.type=='video/mp4'"
+              src="../../../../assets/icon/classroomObservation/videoFile_icon.svg"
+            />
+          </div>
+          <div class="bcc_r_t_l_message">
+            <div>
+              <span>{{ cardData.jsonData.baseMessage.courseName }}</span>
+              <img
+                src="../../../../assets/icon/classroomObservation/table_edit.svg"
+                @click="editBaseMessage"
+              />
+            </div>
+            <span>{{ cardData.create_at }}</span>
+          </div>
+        </div>
+        <div class="bcc_r_t_right">
+          <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_wait" v-if="cardData.status == '0'"
+            >等待处理</span
+          >
+        </div>
+      </div>
+      <div class="bcc_r_bottom">
+        <div class="bcc_r_b_left">
+          <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)"
+            >前往编辑</el-button
+          >
+          <!-- <el-button size="small" @click="lookReport">查看报告</el-button> -->
+          <el-button
+            size="small"
+            @click="regenerate"
+            v-if="[2, 3].includes(cardData.status)"
+            >重新生成</el-button
+          >
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: {
+    data: {
+      type: Object,
+      default: () => {
+        return {};
+      }
+    },
+    isSelect:{
+      type:Boolean,
+      default:false
+    },
+  },
+  data() {
+    return {
+      checked: false,
+      cardData: {
+        id: "1",
+        name: "文件名称文件名称文件名称",
+        create_at: "2025-05-07 16.05.03",
+        remarks: "备注1",
+        status: "0",
+        jsonData: {
+          baseMessage: {
+            courseName: "",
+            teacherName: "",
+            time: "",
+            grade: "",
+            subject: "",
+            textbook: "",
+            studentNum: 0,
+            imageList: {
+              fileList1: [],
+              fileList2: [],
+              fileList3: [],
+              NephogramList: [],
+              videoList: []
+            }
+          },
+          tagList: [
+            { value: 0, name: "通用课堂分析", loading: false },
+            { value: 1, name: "学科课堂分析", loading: false },
+            { value: 2, name: "扩展分析", loading: false }
+          ],
+          steps: [
+            {
+              type: "uploadFile",
+              text: "上传文件",
+              status: "0",
+              progress: "0"
+            },
+            {
+              type: "transcription",
+              text: "文本转录",
+              status: "0",
+              progress: "0"
+            },
+            {
+              type: "automaticCoding",
+              text: "自动编码",
+              status: "0",
+              progress: "0"
+            },
+            {
+              type: "generateReport",
+              text: "生成报告",
+              status: "0",
+
+              progress: "0"
+            }
+          ],
+          analysisList: [],
+          fileData: {
+            name: "文件名称",
+            url: "",
+            type: "text/plain",
+            size: "10kb"
+          },
+          automaticCoding: false
+        }
+      }
+    };
+  },
+  watch: {
+    isSelect(newValue){
+      this.checked = newValue;
+    },
+    // checked(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});
+    //   }
+    // },
+    data: {
+      deep: true,
+      handler(newValue) {
+        if (newValue) {
+          this.cardData = JSON.parse(JSON.stringify(newValue));
+          this.$forceUpdate()
+        }
+      }
+    },
+    // cardData(newValue) {
+    //   if (JSON.stringify(this.data) != JSON.stringify(newValue)) {
+    //     this.$emit("changeData", {});
+    //   }
+    // }
+  },
+  methods: {
+    editBaseMessage() {
+      this.$emit("editBaseMessage",this.cardData.id);
+    },
+    //前往编辑
+    goToEdit() {
+      this.$message.info("前往编辑");
+    },
+    //查看报告
+    lookReport() {
+      this.$message.info("查看报告");
+    },
+    //重新生成
+    regenerate() {
+      this.$message.info("重新生成");
+    },
+    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});
+      } else {
+        this.$emit("changeChecked", {type:1,id:this.cardData.id});
+      }
+    },
+  },
+  mounted() {
+    if (this.data) {
+      this.cardData = JSON.parse(JSON.stringify(this.data));
+    }
+  }
+};
+</script>
+
+<style scoped>
+.batchClassCard {
+  width: 100%;
+  height: 130px;
+  margin: 20px 0;
+  border-radius: 15px;
+  box-sizing: border-box;
+  border: 1px solid rgba(150, 155, 163, 0.4);
+  padding: 20px;
+  display: flex;
+  align-items: center;
+  background-color: #fff;
+}
+
+.bcc_left {
+  width: 50px;
+  height: 100%;
+  display: flex;
+  box-sizing: border-box;
+  padding-left: 10px;
+  /* justify-content: center; */
+  align-items: center;
+}
+
+.bcc_right {
+  width: calc(100% - 50px);
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.bcc_r_top {
+  width: 100%;
+  height: 60px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.bcc_r_t_left {
+  width: calc(100% - 100px);
+  height: 100%;
+  display: flex;
+  align-items: center;
+}
+
+.bcc_r_t_l_image {
+  width: 50px;
+  height: 50px;
+}
+
+.bcc_r_t_l_image > img {
+  width: 45px;
+  height: 45px;
+  object-fit: cover;
+}
+.bcc_r_t_right {
+  width: 100px;
+  height: 100%;
+  display: flex;
+  align-items: flex-start;
+  justify-content: flex-end;
+  cursor: pointer;
+}
+
+.bcc_r_t_right > span {
+  font-size: 16px;
+}
+
+.bcc_r_t_l_message {
+  width: 100%;
+  height: 50px;
+}
+
+.bcc_r_t_l_message > div {
+  width: 100%;
+  height: auto;
+  display: flex;
+  align-items: center;
+}
+
+.bcc_r_t_l_message > div > span {
+  max-width: calc(100% - 70px);
+  text-overflow: ellipsis;
+  overflow: hidden;
+  white-space: nowrap;
+  font-size: 16px;
+  color: #000;
+}
+
+.bcc_r_t_l_message > div > img {
+  width: 18px;
+  height: 18px;
+  margin-left: 10px;
+  cursor: pointer;
+}
+
+.bcc_r_t_l_message > span {
+  margin-top: 10px;
+  font-size: 14px;
+  display: block;
+  color: rgba(150, 155, 163, 1);
+}
+
+.bcc_r_bottom {
+  width: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  height: calc(100% - 60px);
+}
+
+.bcc_r_b_left {
+  /* width: calc(100% - 200px); */
+  /* width: calc(100% - 290px); */
+  flex: 1;
+  height: 100%;
+  border-radius: 5px;
+  box-sizing: border-box;
+  border: dashed 1px rgba(150, 155, 163, 1);
+  display: flex;
+  align-items: center;
+}
+
+.bcc_r_b_left > span {
+  display: block;
+  width: 100%;
+  height: 100%;
+  line-height: 25px;
+}
+
+.bcc_r_b_left >>> .el-input {
+  width: 100%;
+  height: 100%;
+}
+
+.bcc_r_b_left >>> .el-input > .el-input__inner {
+  width: 100%;
+  height: 100%;
+  border: none;
+  outline: none;
+}
+
+.bcc_r_b_right {
+  width: auto;
+  padding: 0 0 0 10px;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+}
+
+.status_success {
+  color: rgba(188, 230, 133, 1);
+}
+
+.status_doing {
+  color: rgba(54, 129, 252, 1);
+}
+
+.status_wait {
+  color: rgba(150, 155, 163, 1);
+}
+
+.status_fail {
+  color: rgb(185, 2, 2);
+}
+</style>

+ 327 - 0
src/components/pages/classroomObservation/newComponents/uploadFile.vue

@@ -0,0 +1,327 @@
+<template>
+	<div class="uploadBox"></div>
+</template>
+
+<script>
+import "../../../../common/aws-sdk-2.235.1.min.js";
+export default {
+	props:{
+		index:{
+			type:String,
+			default:"0"
+		},
+	},
+	data() {
+		return {
+			bucket: "", //aws上传接口
+			bucketname: "ccrb", //桶
+			uploadid: "",
+			partsize: 10 * 1024 * 1024, //10MB  分片
+			filestate: {
+				status: "", //有 error(直接报错) 和 fail(上传的时候报错) 和 success(上传成功) 和 processing(上传中)
+				percent: 0, //(0-100的进度)
+			},
+			file: null,
+			flag:true,
+		};
+	},
+	watch: {
+		filestate: {
+			handler(newValue) {
+				this.$emit("progressUpdate", {index:this.index,...newValue});
+			},
+			immediate: true,
+			deep: true,
+		},
+	},
+	methods: {
+		//--------------------------分断上传保证稳定性
+		//初始化上传
+		// async init(){
+		// 	const credentials = {
+		// 	    accessKeyId: "AKIATLPEDU37QV5CHLMH",
+		// 	    secretAccessKey: "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",
+		// 	}; //秘钥形式的登录上传
+		// 	window.AWS.config.update(credentials);
+		// 	window.AWS.config.region = "cn-northwest-1"; //设置区域
+		// 	//桶的设置
+		// 	bucket = new window.AWS.S3({
+		// 	    params: {
+		// 	        Bucket: this.bucketname
+		// 	    }
+		// 	});
+		// 	return bucket;
+		// },
+		// 初始化上传入口
+		async initMultipartUpload(key, file) {
+			const params = {
+				Bucket: this.bucketname,
+				Key: key,
+				ContentType: file.type,
+				ACL: "public-read",
+			};
+			//创建一个续传通道
+			const data = await this.bucket.createMultipartUpload(params).promise();
+			return data.UploadId;
+		},
+		// 上传文件的某一部分
+		async uploadPart(file, keyname, uploadid, pn, start, end) {
+			//key可以设置为桶的相对路径,Body为文件, ACL最好要设置
+			if(!this.flag)return;
+			var params = {
+				Bucket: this.bucketname,
+				Key: keyname,
+				// ContentType: file.type,
+				PartNumber: pn,
+				UploadId: uploadid,
+				Body: file.slice(start, end),
+				// "Access-Control-Allow-Credentials": "*",
+				// ACL: "public-read",
+			};
+			const result = await this.bucket.uploadPart(params).promise();
+			return { ETag: result.ETag, PartNumber: pn };
+		},
+		//完成分块上传
+		async completeMultipartUpload(parts, keyname, uploadid) {
+			if(!this.flag)return;
+			const params = {
+				Bucket: this.bucketname,
+				Key: keyname,
+				MultipartUpload: { Parts: parts },
+				UploadId: uploadid,
+			};
+			return await this.bucket.completeMultipartUpload(params).promise();
+		},
+		async abortMultipartUpload(key, uploadid) {
+			const params = {
+				Bucket: this.bucketname,
+				Key: key,
+				UploadId: uploadid,
+			};
+			let data = await this.bucket.abortMultipartUpload(params).promise();
+			this.$emit("delUpload",{index:this.index})
+
+		},
+		//--------------------------------下面支持断点续传
+
+		//初始化亚马逊参数
+		async init() {
+			//秘钥形式的登录上传
+			const credentials = {
+				accessKeyId: "AKIATLPEDU37QV5CHLMH",
+				secretAccessKey: "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",
+				region: "cn-northwest-1",
+			};
+			window.AWS.config.update(credentials);
+			// window.AWS.config.region = "cn-northwest-1"; //设置区域
+			//桶的设置
+			this.bucket = new window.AWS.S3({
+				params: {
+					Bucket: this.bucketname,
+				},
+			});
+			return this.bucket;
+		},
+
+		//获取当前文件是否有已上传断点信息
+		async getawscheckpoint(key) {
+			let partsinfo;
+			try {
+				const result = await this.bucket
+					.listMultipartUploads({ Bucket: this.bucketname, Prefix: key })
+					.promise();
+				//获取具体分片信息
+				if (result.Uploads.length) {
+					this.uploadid = result.Uploads[result.Uploads.length - 1].UploadId;
+					partsinfo = await this.bucket
+						.listParts({
+							Bucket: this.bucketname,
+							Key: key,
+							UploadId: this.uploadid,
+						})
+						.promise();
+				}
+			} catch (err) {
+				console.log(err);
+			}
+			return { uploadid: this.uploadid, partsinfo };
+		},
+
+		//分段上传
+		async awsuploadpart(filestate, file, uploadid, parts, key) {
+			var partarr = []; //已完成的数组
+			//已完成的分片,转化成提交格式
+			const completeparts = parts.map((_) => {
+				partarr.push(_.PartNumber);
+				return { PartNumber: _.PartNumber, ETag: _.ETag };
+			});
+			// 分块上传文件
+			// parts = [];
+			let uploadpart;
+			let start = 0;
+			let end = 0;
+			let len = Math.ceil(file.size / this.partsize); //循环的长度
+			if (partarr.length) {
+				this.filestate.status = "processing";
+				this.filestate.percent = parseInt((completeparts.length * 100) / len);
+			}
+			//循环上传
+			for (let i = 0; i < len; i++) {
+				if(!this.flag)break;
+				start = i * this.partsize;
+				end = (i + 1) * this.partsize;
+				if (!partarr.includes(i+1)) {
+
+					uploadpart = await this.uploadPart(
+						file,
+						key,
+						uploadid,
+						i + 1,
+						start,
+						end
+					);
+					if (uploadpart.ETag != null) {
+						completeparts.push(uploadpart);
+						partarr.push(uploadpart.PartNumber);
+						this.filestate.status = "processing";
+						this.filestate.percent = parseInt(
+							(completeparts.length * 100) / len
+						);
+					} else {
+						this.filestate.status = "fail";
+						this.stopUpload();
+						return;
+					}
+				}
+			}
+			//提交上传成功信息
+			if(this.flag){
+					let data = await this.completeMultipartUpload(
+					completeparts,
+					key,
+					uploadid
+				);
+				this.filestate.status = "success";
+				return data;
+			}
+		},
+
+		//上传的接口
+		async awsupload({ file = this.file, keyName, folderName }) {
+			if (!file) return this.$message.error("请上传文件");
+			this.init(); //初始化桶
+			// const key = (folderid || uuidv4()) + "/" + file.name; //需要上传的文件名
+			let key = "";
+			if (keyName) {
+				key = keyName;
+			} else {
+				if (folderName) {
+					key = `${folderName}/${file.name}`;
+				} else {
+					key = `default/${file.name.split(".")[0] +
+							new Date().getTime() +
+							"." +
+							file.name.split(".")[file.name.split(".").length - 1]}`;
+				}
+			}
+			this.filestate = {
+				percent: 0,
+				status: "start",
+			};
+			this.filestate.percent = 0;
+			this.filestate.status = "start";
+			this.file = file;
+			//上传的参数
+			var params = {
+				Bucket: this.bucketname,
+				Key: key,
+			};
+			this.flag = true;
+			//设置桶上传文件
+			try {
+				//检查文件是否已上传
+				this.bucket.headObject(params, async (err, data) => {
+					// 没有上传成功,head方法会返回失败
+					if (err) {
+						//检查是否部分上传
+						const { uploadid, partsinfo } = await this.getawscheckpoint(
+							key,
+							this.bucket
+						);
+						//如果已经部分存在,那么直接在节点续传
+						if (uploadid) {
+							//断点续传
+							this.$emit("startUpload", { index:this.index,key, uploadid });
+							this.filestate.key = key;
+							this.filestate.uploadid = uploadid;
+							this.flag = true;
+							let data = await this.awsuploadpart(
+								this.filestate,
+								file,
+								uploadid,
+								partsinfo.Parts,
+								key
+							);
+							if(this.flag || this.filestate.percent==100)return this.$emit("success", {index:this.index, data, key, uploadid });
+							// return {data,key,uploadid}
+						}
+						//不存在,上传新的
+						else {
+							const uploadid = await this.initMultipartUpload(key, file); //初始化文件上传
+							this.$emit("startUpload", { index:this.index,key, uploadid });
+							this.filestate.key = key;
+							this.filestate.uploadid = uploadid;
+							this.flag = true;
+							let data = await this.awsuploadpart(
+								this.filestate,
+								file,
+								uploadid,
+								[],
+								key
+							);
+							if(this.flag || this.filestate.percent==100)return this.$emit("success", {index:this.index, data, key, uploadid });
+							// return {data,key,uploadid}
+						}
+					}
+					//如果已经上传成功了,那么直接返回状态百分百
+					else if (data) {
+						//data存在,上传成功
+						this.filestate.percent = 100;
+						this.filestate.status = "success";
+						let url = `https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/${key}`
+						this.file = null;
+						if(this.flag)return this.$emit("success", { index:this.index,data:{
+							Key:key,
+							Location:url,
+							ETag:data.ETag,
+							size:data.ContentLength,
+							ServerSideEncryption:data.ServerSideEncryption,
+							Bucket:this.bucketname
+							} });
+						// return {data,key,uploadid}
+					}
+				});
+			} catch (err) {
+				this.filestate.status = "error";
+				this.stopUpload();
+				console.log(err);
+			}
+		},
+		// 停止上传
+		stopUpload(){
+			this.filestate.status = "stop";
+			this.flag = false;
+		}
+	},
+	mounted() {
+		this.bucket = "";
+		this.file = null;
+	},
+};
+</script>
+
+<style scoped>
+.uploadBox {
+	display: none;
+}
+</style>

+ 1 - 0
src/components/pages/test/check/docxTemplateDialog.vue

@@ -533,6 +533,7 @@ export default {
             );
           } else if (fieldList[i].type == "file") {
             let _text = ``;
+            if(!fieldList[i].value || !fileList[i].value.length<=0)continue;
             // this.fieldList[i].value.forEach(async f => {
             for (let j = 0; j < fieldList[i].value.length; j++) {
               let f = fieldList[i].value[j];

+ 18 - 2
src/components/pages/test/examine/conpoments/targetPage.vue

@@ -237,20 +237,24 @@
             <template slot-scope="scope">
               <div class="ScoreStep">
                 <el-input
+                class="num_input"
                   v-if="scope.row.judge == 1"
                   style=" color
                 :#A7AAB2;"
                   @blur="Submit(scope.row)"
-                  v-model.number="scope.row.cogSco"
+                  v-model="scope.row.cogSco"
+                  type="number"
                   v-stopMousewheel
                 ></el-input>
                 <el-input
                   v-else
+                  class="num_input"
                   :style="{
                     color: scope.row.cogSco == scope.row.evaSca ? '#000' : 'red'
                   }"
                   @blur="Submit(scope.row)"
-                  v-model.number="scope.row.cogSco"
+                  v-model="scope.row.cogSco"
+                  type="number"
                   v-stopMousewheel
                 ></el-input>
               </div>
@@ -2136,4 +2140,16 @@ input[type="number"] {
 .dialog_diy >>> .el-dialog__footer {
   background: #fafafa;
 }
+
+.num_input>>>input{
+  padding-right: 0px;
+  -moz-appearance: textfield;
+  -webkit-appearance: textfield;
+  line-height: 1px !important;
+}
+
+.num_input>>>input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
 </style>

+ 17 - 2
src/components/pages/testPerson/examine/index.vue

@@ -96,18 +96,20 @@
                   <div>
                     <div v-if="k.type == 1" class="ScoreStep">
                       <el-input
+                        class="num_input"
                         v-if="allData.type * 1 != 2"
                         @blur="
                           saveTab(
-                            k.sco1 > k.score
+                            parseFloat(k.sco1) > k.score
                               ? (k.sco1 = k.score)
                               : '' || k.sco1 < 0
                               ? (k.sco1 = '')
                               : ''
                           )
                         "
-                        v-model.number="k.sco1"
+                        v-model="k.sco1"
                         v-stopMousewheel
+                        type="number"
                       ></el-input>
                       <div v-else>{{ k.sco1 }}</div>
                     </div>
@@ -364,6 +366,7 @@ export default {
     },
     // 保存
     saveTab() {
+      this.$forceUpdate();
       let PageBaseDataCopy = JSON.parse(JSON.stringify(this.PageBaseData));
 
       for (const e of PageBaseDataCopy) {
@@ -921,4 +924,16 @@ input[type="number"] {
 .dialog_diy >>> .el-dialog__footer {
   background: #fafafa;
 }
+
+.num_input>>>input{
+  padding-right: 0px;
+  -moz-appearance: textfield;
+  -webkit-appearance: textfield;
+  line-height: 1px !important;
+}
+
+.num_input>>>input[type="number"]::-webkit-inner-spin-button, input[type="number"]::-webkit-outer-spin-button {
+  -webkit-appearance: none;
+  margin: 0;
+}
 </style>

Nem az összes módosított fájl került megjelenítésre, mert túl sok fájl változott