Ver Fonte

Merge branch 'beta'

lsc há 11 meses atrás
pai
commit
549b1b6305
43 ficheiros alterados com 3237 adições e 876 exclusões
  1. 1 1
      dist/index.html
  2. 0 0
      dist/static/css/app.3faebb4ab7bb2a1bb7307791b753954b.css
  3. 0 0
      dist/static/css/app.3faebb4ab7bb2a1bb7307791b753954b.css.map
  4. 0 0
      dist/static/css/app.92fc15c5674f3229ff8e994cf5744006.css
  5. 0 0
      dist/static/css/app.92fc15c5674f3229ff8e994cf5744006.css.map
  6. 106 0
      dist/static/img/ropt.c731819.svg
  7. 0 0
      dist/static/js/app.e6dab380549fd9937e78.js
  8. 0 0
      dist/static/js/app.e6dab380549fd9937e78.js.map
  9. 0 0
      dist/static/js/manifest.571c38d63f24b1ae9e16.js.map
  10. 0 0
      dist/static/js/vendor.77a666acfa8fe74a3d07.js
  11. 0 0
      dist/static/js/vendor.77a666acfa8fe74a3d07.js.map
  12. 137 0
      package-lock.json
  13. 3 0
      package.json
  14. BIN
      src/assets/icon/classroomObservation/voiceCard.png
  15. 1 0
      src/assets/icon/course/arrow.svg
  16. 106 0
      src/assets/icon/exportPdfworks/ropt.svg
  17. 295 171
      src/components/pages/aiAddCourse/addCourse.vue
  18. 21 14
      src/components/pages/aiAddCourse/aiBoxRight.vue
  19. 29 4
      src/components/pages/aiAddCourse/aiCreateDialog.vue
  20. 7 0
      src/components/pages/aiAddCourse/evaList.vue
  21. 8 0
      src/components/pages/classroomObservation/components/addNewAnalysisDialog.vue
  22. 352 53
      src/components/pages/classroomObservation/components/analysis.vue
  23. 3 3
      src/components/pages/classroomObservation/components/analysis2.vue
  24. 176 33
      src/components/pages/classroomObservation/components/analysisItem.vue
  25. 2 2
      src/components/pages/classroomObservation/components/analysisItem2.vue
  26. 124 6
      src/components/pages/classroomObservation/components/analysisSpecialItem.vue
  27. 1 1
      src/components/pages/classroomObservation/components/analysisTemplateDialog.vue
  28. 3 0
      src/components/pages/classroomObservation/components/chatArea.vue
  29. 150 204
      src/components/pages/classroomObservation/components/messageArea.vue
  30. 161 36
      src/components/pages/classroomObservation/components/startPage.vue
  31. 20 53
      src/components/pages/classroomObservation/index.vue
  32. 775 205
      src/components/pages/components/exportDataDialog.vue
  33. 1 1
      src/components/pages/student/addCourse.vue
  34. 1 1
      src/components/pages/studentManage/student.vue
  35. 1 1
      src/components/pages/studio/addCourse.vue
  36. 50 44
      src/components/pages/synergyCourse/addCourse.vue
  37. 76 24
      src/components/pages/test/check/aiBoxRight.vue
  38. 557 0
      src/components/pages/test/check/docxTemplateDialog.vue
  39. 15 3
      src/components/pages/test/check/index.vue
  40. 9 0
      src/components/pages/test/dataCom/cascader.vue
  41. 28 8
      src/components/pages/test/dataCom/radarZong.vue
  42. 15 6
      src/components/pages/test/databoard.vue
  43. 3 2
      src/components/pages/works.vue

+ 1 - 1
dist/index.html

@@ -32,7 +32,7 @@
       width: 100%;
       background: #e6eaf0;
       font-family: '黑体';
-    }</style><link href=./static/css/app.92fc15c5674f3229ff8e994cf5744006.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.571c38d63f24b1ae9e16.js></script><script type=text/javascript src=./static/js/vendor.8db9a3f753f5644e3c5e.js></script><script type=text/javascript src=./static/js/app.46667317cf727bb6b265.js></script></body></html><script>function stopSafari() {
+    }</style><link href=./static/css/app.3faebb4ab7bb2a1bb7307791b753954b.css rel=stylesheet></head><body><div id=app></div><script type=text/javascript src=./static/js/manifest.571c38d63f24b1ae9e16.js></script><script type=text/javascript src=./static/js/vendor.77a666acfa8fe74a3d07.js></script><script type=text/javascript src=./static/js/app.e6dab380549fd9937e78.js></script></body></html><script>function stopSafari() {
     //阻止safari浏览器双击放大功能
     let lastTouchEnd = 0  //更新手指弹起的时间
     document.documentElement.addEventListener("touchstart", function (event) {

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/static/css/app.3faebb4ab7bb2a1bb7307791b753954b.css


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/static/css/app.3faebb4ab7bb2a1bb7307791b753954b.css.map


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/static/css/app.92fc15c5674f3229ff8e994cf5744006.css


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/static/css/app.92fc15c5674f3229ff8e994cf5744006.css.map


Diff do ficheiro suprimidas por serem muito extensas
+ 106 - 0
dist/static/img/ropt.c731819.svg


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/static/js/app.e6dab380549fd9937e78.js


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/static/js/app.e6dab380549fd9937e78.js.map


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/static/js/manifest.571c38d63f24b1ae9e16.js.map


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/static/js/vendor.77a666acfa8fe74a3d07.js


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
dist/static/js/vendor.77a666acfa8fe74a3d07.js.map


+ 137 - 0
package-lock.json

@@ -13,6 +13,8 @@
         "clipboard": "^2.0.10",
         "cocoroboworkpc": "file:",
         "dayjs": "^1.11.7",
+        "docxtemplater": "^3.50.0",
+        "docxtemplater-image-module-free": "^1.1.1",
         "echarts": "^5.4.2",
         "echarts-wordcloud": "^2.1.0",
         "element-china-area-data": "^5.0.2",
@@ -39,6 +41,7 @@
         "opencc-js": "^1.0.5",
         "papaparse": "^5.4.1",
         "pdfjs-dist": "^2.5.207",
+        "pizzip": "^3.1.7",
         "pptxgenjs": "^3.12.0",
         "qrcodejs2": "^0.0.2",
         "qs": "^6.10.1",
@@ -520,6 +523,14 @@
         "@xtuc/long": "4.2.2"
       }
     },
+    "node_modules/@xmldom/xmldom": {
+      "version": "0.8.10",
+      "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
+      "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==",
+      "engines": {
+        "node": ">=10.0.0"
+      }
+    },
     "node_modules/@xtuc/ieee754": {
       "version": "1.2.0",
       "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -5256,6 +5267,25 @@
         "buffer-indexof": "^1.0.0"
       }
     },
+    "node_modules/docxtemplater": {
+      "version": "3.50.0",
+      "resolved": "https://registry.npmmirror.com/docxtemplater/-/docxtemplater-3.50.0.tgz",
+      "integrity": "sha512-6EqYbBFUcdNKVwS6G8vQ+pFOURJ7zoSvUNASIi4MPnCpkRdYDvmaOV2e1XcScMrEQV5pFZUAAbKi30Z+JTbLFA==",
+      "dependencies": {
+        "@xmldom/xmldom": "^0.8.10"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
+    "node_modules/docxtemplater-image-module-free": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/docxtemplater-image-module-free/-/docxtemplater-image-module-free-1.1.1.tgz",
+      "integrity": "sha512-aWOzVQN7ggDYjfoy3pTTNrcrZ7/CJrQcI9cT+hmyHE6nRLR67nt5yPFPe9hm9VWbfYIED2fi+3itOnF0TE/RWQ==",
+      "dependencies": {
+        "xmldom": "^0.1.27"
+      }
+    },
     "node_modules/dom-converter": {
       "version": "0.2.0",
       "resolved": "https://registry.npmmirror.com/dom-converter/download/dom-converter-0.2.0.tgz",
@@ -11396,6 +11426,19 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/pizzip": {
+      "version": "3.1.7",
+      "resolved": "https://registry.npmmirror.com/pizzip/-/pizzip-3.1.7.tgz",
+      "integrity": "sha512-VemVeAQtdIA74AN1Fsd5OmbMbEeS4YOwwlcudgzvmUrOIOPrk1idYC5Tw5FUFq/I0c26ziNOw9z//iPmGfp1jA==",
+      "dependencies": {
+        "pako": "^2.1.0"
+      }
+    },
+    "node_modules/pizzip/node_modules/pako": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz",
+      "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
+    },
     "node_modules/pkcs7": {
       "version": "0.2.3",
       "resolved": "https://registry.npmmirror.com/pkcs7/download/pkcs7-0.2.3.tgz",
@@ -19126,6 +19169,15 @@
         "node": ">=0.8"
       }
     },
+    "node_modules/xmldom": {
+      "version": "0.1.31",
+      "resolved": "https://registry.npmmirror.com/xmldom/-/xmldom-0.1.31.tgz",
+      "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ==",
+      "deprecated": "Deprecated due to CVE-2021-21366 resolved in 0.5.0",
+      "engines": {
+        "node": ">=0.1"
+      }
+    },
     "node_modules/xmlhttprequest": {
       "version": "1.8.0",
       "resolved": "https://registry.npmmirror.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
@@ -19635,6 +19687,11 @@
         "@xtuc/long": "4.2.2"
       }
     },
+    "@xmldom/xmldom": {
+      "version": "0.8.10",
+      "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
+      "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw=="
+    },
     "@xtuc/ieee754": {
       "version": "1.2.0",
       "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -21779,6 +21836,8 @@
         "copy-webpack-plugin": "^4.0.1",
         "css-loader": "^0.28.0",
         "dayjs": "^1.11.7",
+        "docxtemplater": "*",
+        "docxtemplater-image-module-free": "*",
         "echarts": "^5.4.2",
         "echarts-wordcloud": "^2.1.0",
         "element-china-area-data": "^5.0.2",
@@ -21813,6 +21872,7 @@
         "ora": "^1.2.0",
         "papaparse": "^5.4.1",
         "pdfjs-dist": "^2.5.207",
+        "pizzip": "*",
         "portfinder": "^1.0.13",
         "postcss-import": "^11.0.0",
         "postcss-loader": "^2.0.8",
@@ -22234,6 +22294,11 @@
             "@xtuc/long": "4.2.2"
           }
         },
+        "@xmldom/xmldom": {
+          "version": "0.8.10",
+          "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
+          "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw=="
+        },
         "@xtuc/ieee754": {
           "version": "1.2.0",
           "resolved": "https://registry.npmmirror.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -26276,6 +26341,22 @@
             "buffer-indexof": "^1.0.0"
           }
         },
+        "docxtemplater": {
+          "version": "3.50.0",
+          "resolved": "https://registry.npmmirror.com/docxtemplater/-/docxtemplater-3.50.0.tgz",
+          "integrity": "sha512-6EqYbBFUcdNKVwS6G8vQ+pFOURJ7zoSvUNASIi4MPnCpkRdYDvmaOV2e1XcScMrEQV5pFZUAAbKi30Z+JTbLFA==",
+          "requires": {
+            "@xmldom/xmldom": "^0.8.10"
+          }
+        },
+        "docxtemplater-image-module-free": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/docxtemplater-image-module-free/-/docxtemplater-image-module-free-1.1.1.tgz",
+          "integrity": "sha512-aWOzVQN7ggDYjfoy3pTTNrcrZ7/CJrQcI9cT+hmyHE6nRLR67nt5yPFPe9hm9VWbfYIED2fi+3itOnF0TE/RWQ==",
+          "requires": {
+            "xmldom": "^0.1.27"
+          }
+        },
         "dom-converter": {
           "version": "0.2.0",
           "resolved": "https://registry.npmmirror.com/dom-converter/download/dom-converter-0.2.0.tgz",
@@ -31199,6 +31280,21 @@
             "pinkie": "^2.0.0"
           }
         },
+        "pizzip": {
+          "version": "3.1.7",
+          "resolved": "https://registry.npmmirror.com/pizzip/-/pizzip-3.1.7.tgz",
+          "integrity": "sha512-VemVeAQtdIA74AN1Fsd5OmbMbEeS4YOwwlcudgzvmUrOIOPrk1idYC5Tw5FUFq/I0c26ziNOw9z//iPmGfp1jA==",
+          "requires": {
+            "pako": "^2.1.0"
+          },
+          "dependencies": {
+            "pako": {
+              "version": "2.1.0",
+              "resolved": "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz",
+              "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
+            }
+          }
+        },
         "pkcs7": {
           "version": "0.2.3",
           "resolved": "https://registry.npmmirror.com/pkcs7/download/pkcs7-0.2.3.tgz",
@@ -37489,6 +37585,11 @@
             "word": "~0.3.0"
           }
         },
+        "xmldom": {
+          "version": "0.1.31",
+          "resolved": "https://registry.npmmirror.com/xmldom/-/xmldom-0.1.31.tgz",
+          "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ=="
+        },
         "xmlhttprequest": {
           "version": "1.8.0",
           "resolved": "https://registry.npmmirror.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
@@ -39524,6 +39625,22 @@
         "buffer-indexof": "^1.0.0"
       }
     },
+    "docxtemplater": {
+      "version": "3.50.0",
+      "resolved": "https://registry.npmmirror.com/docxtemplater/-/docxtemplater-3.50.0.tgz",
+      "integrity": "sha512-6EqYbBFUcdNKVwS6G8vQ+pFOURJ7zoSvUNASIi4MPnCpkRdYDvmaOV2e1XcScMrEQV5pFZUAAbKi30Z+JTbLFA==",
+      "requires": {
+        "@xmldom/xmldom": "^0.8.10"
+      }
+    },
+    "docxtemplater-image-module-free": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/docxtemplater-image-module-free/-/docxtemplater-image-module-free-1.1.1.tgz",
+      "integrity": "sha512-aWOzVQN7ggDYjfoy3pTTNrcrZ7/CJrQcI9cT+hmyHE6nRLR67nt5yPFPe9hm9VWbfYIED2fi+3itOnF0TE/RWQ==",
+      "requires": {
+        "xmldom": "^0.1.27"
+      }
+    },
     "dom-converter": {
       "version": "0.2.0",
       "resolved": "https://registry.npmmirror.com/dom-converter/download/dom-converter-0.2.0.tgz",
@@ -44447,6 +44564,21 @@
         "pinkie": "^2.0.0"
       }
     },
+    "pizzip": {
+      "version": "3.1.7",
+      "resolved": "https://registry.npmmirror.com/pizzip/-/pizzip-3.1.7.tgz",
+      "integrity": "sha512-VemVeAQtdIA74AN1Fsd5OmbMbEeS4YOwwlcudgzvmUrOIOPrk1idYC5Tw5FUFq/I0c26ziNOw9z//iPmGfp1jA==",
+      "requires": {
+        "pako": "^2.1.0"
+      },
+      "dependencies": {
+        "pako": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz",
+          "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug=="
+        }
+      }
+    },
     "pkcs7": {
       "version": "0.2.3",
       "resolved": "https://registry.npmmirror.com/pkcs7/download/pkcs7-0.2.3.tgz",
@@ -50737,6 +50869,11 @@
         "word": "~0.3.0"
       }
     },
+    "xmldom": {
+      "version": "0.1.31",
+      "resolved": "https://registry.npmmirror.com/xmldom/-/xmldom-0.1.31.tgz",
+      "integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ=="
+    },
     "xmlhttprequest": {
       "version": "1.8.0",
       "resolved": "https://registry.npmmirror.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",

+ 3 - 0
package.json

@@ -15,6 +15,8 @@
     "clipboard": "^2.0.10",
     "cocoroboworkpc": "file:",
     "dayjs": "^1.11.7",
+    "docxtemplater": "^3.50.0",
+    "docxtemplater-image-module-free": "^1.1.1",
     "echarts": "^5.4.2",
     "echarts-wordcloud": "^2.1.0",
     "element-china-area-data": "^5.0.2",
@@ -41,6 +43,7 @@
     "opencc-js": "^1.0.5",
     "papaparse": "^5.4.1",
     "pdfjs-dist": "^2.5.207",
+    "pizzip": "^3.1.7",
     "pptxgenjs": "^3.12.0",
     "qrcodejs2": "^0.0.2",
     "qs": "^6.10.1",

BIN
src/assets/icon/classroomObservation/voiceCard.png


+ 1 - 0
src/assets/icon/course/arrow.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="1727575870183" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4244" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M312.888889 995.555556c-17.066667 0-28.444444-5.688889-39.822222-17.066667-22.755556-22.755556-17.066667-56.888889 5.688889-79.644445l364.088888-329.955555c11.377778-11.377778 17.066667-22.755556 17.066667-34.133333 0-11.377778-5.688889-22.755556-17.066667-34.133334L273.066667 187.733333c-22.755556-22.755556-28.444444-56.888889-5.688889-79.644444 22.755556-22.755556 56.888889-28.444444 79.644444-5.688889l364.088889 312.888889c34.133333 28.444444 56.888889 73.955556 56.888889 119.466667s-17.066667 85.333333-51.2 119.466666l-364.088889 329.955556c-11.377778 5.688889-28.444444 11.377778-39.822222 11.377778z" fill="#000000" p-id="4245"></path></svg>

Diff do ficheiro suprimidas por serem muito extensas
+ 106 - 0
src/assets/icon/exportPdfworks/ropt.svg


Diff do ficheiro suprimidas por serem muito extensas
+ 295 - 171
src/components/pages/aiAddCourse/addCourse.vue


+ 21 - 14
src/components/pages/aiAddCourse/aiBoxRight.vue

@@ -259,7 +259,7 @@
 
       <span
         class="c_voiceBtn"
-        style="right: 70px;"
+        :style="{right: faloading ? '85px' : '70px'}"
         v-if="!courseText && isVoice && !isTalk"
         @click.stop="changeVoice(false)"
       >
@@ -381,7 +381,6 @@ import checkIsImg from "../../../assets/icon/sourceFile/check_is.png";
 import { v4 as uuidv4 } from "uuid";
 import MarkdownIt from "markdown-it";
 import TurndownService from "turndown";
-import { number } from "echarts";
 
 const OpenCC = require("opencc-js");
 let converter = OpenCC.Converter({
@@ -524,7 +523,7 @@ export default {
       saveUid: "",
       isVoice: false,
       isTalk: false,
-      username: ""
+      username: "",
     };
   },
   watch: {
@@ -710,6 +709,8 @@ export default {
       if (this.courseText.trim().length == 0)
         return this.$message.error("请输入内容");
       let message = this.courseText;
+      this.$emit('addCourseBehavior', 'courseBehavior', '点击对话框-发送按钮')
+
       if (this.courseText) {
         let msg = ``;
         if (this.checkArray.length) {
@@ -774,10 +775,10 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'.Output format carefully reference
         this.faloading = true;
         if (_atRoleList.length > 0) {
           //有@角色
-          let _replaceText = `Language: ${this.getLang()}
-            Language: ${this.getLang()}
-            Language: ${this.getLang()}
-            ${message}`;
+          let _replaceText = `NOTICE
+Language: ${this.getLang()}
+
+${message}`;
           let _htmlText = message;
           _atRoleList.forEach(_i => {
             _replaceText = _replaceText.replaceAll(`@${_i.assistantName}`, ``);
@@ -878,7 +879,13 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'.Output format carefully reference
               if (i.content)
                 history.push({
                   role: "user",
-                  content: index == this.array.length - 1 ? message : i.content
+                  content: index == this.array.length - 1 ? `NOTICE
+Language: ${this.getLang()}
+
+${message}` : `NOTICE
+Language: ${this.getLang()}
+
+${i.content}`
                 });
               if (i.aiContent)
                 history.push({ role: "assistant", content: i.aiContent });
@@ -894,10 +901,10 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'.Output format carefully reference
 
           history = history.map(i => ({
             role: i.role,
-            content: `Language: ${this.getLang()}
-            Language: ${this.getLang()}
-            Language: ${this.getLang()}
-            ${i.content}`
+            content: `NOTICE
+Language: ${this.getLang()}
+
+${i.content}`
           }));
           this.$nextTick(() => {
             this.$refs.chatDialog.scrollTop = this.$refs.chatDialog.scrollHeight;
@@ -1695,11 +1702,11 @@ ATTENTION: Use '##' to SPLIT SECTIONS, not '#'.Output format carefully reference
   margin: 10px 5px 10px 5px;
   min-height: 35px;
   height: 35px;
-  max-height: 60vh;
+  max-height: 100px;
   border: none;
   outline: none;
   resize: none;
-  font-size: 18px;
+  font-size: 16px;
   overflow: auto;
   padding-right: 100px;
   background-color: #fff !important;

+ 29 - 4
src/components/pages/aiAddCourse/aiCreateDialog.vue

@@ -45,6 +45,12 @@
 import Pptxgen from "pptxgenjs";
 import wOffice from "../components/wOffice.vue";
 import { v4 as uuidv4 } from "uuid";
+var OpenCC = require("opencc-js");
+
+let converter2 = OpenCC.Converter({
+		from:'cn',
+		to:'hk'
+})
 
 export default {
     components: {
@@ -79,6 +85,10 @@ export default {
         },
         unitJson: {
             type: Array,
+        },
+        languageSetting: {
+            type: Number,
+            default: 0,
         }
     },
     // 根据用户给你的参考资料
@@ -834,6 +844,17 @@ export default {
         },
     },
     methods: {
+        getLang(){
+            let lang = ''
+            if(this.languageSetting == 0){
+                lang = 'Chinese.'
+            }else if(this.languageSetting == 1){
+                lang = 'Traditional Chinese.'
+            }else if(this.languageSetting == 2){
+                lang = 'English.'
+            }
+            return lang
+        },
         cancelAjax(){
             this.$message.success("已经成功停止")
             this.stopPpt.cancel('Request canceled by the user.');
@@ -942,7 +963,7 @@ export default {
                 message = `NOTICE
 Role: 你是ppt内容设计大师,能力是从用户提供的文件资料中提取最重要的学科概念作为ppt参考内容,最后根据Context要求的流程要求输出ppt内容。
 Output: Provide your output in json format.
-Language: Please use the same language as the user requirement, if the user speaks Chinese, the specific text of your answer should also be in Chinese.
+Language: ${this.getLang()}
 ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
 Instruction: Based on the context, follow "Format example", write content.
 
@@ -966,7 +987,9 @@ ${_this.outline.replaceAll('#', '').replaceAll('*', '').replaceAll('-', '').repl
 [{"page": "页码(数字)","title": "学科概念(请从给你的大纲中摘取)(标题)","task": "知识点(请从给你的大纲中摘取)(子标题)","points": "内容:用亲切的口吻告诉学生本步骤应做什么?或者是用亲切的口吻向学生介绍知识点,多条时可使用bullet point;或者是给学生测试题。"}]`;
             } else {
                 if (this.courseState == 4) {
-                    message = `# 任务
+                    message = `NOTICE Language: ${this.getLang()}
+
+# 任务
 请根据参考资料,生成关于${this.courseName},为教师生成这节课的教学ppt的大纲,大纲的主要内容课程知识点的讲解与相关练习和测试。你的输出应该符合#输出格式
 
 ${mclass.length ? "#参考资料\n面向年级:" + mclass.join(",") : ""}
@@ -980,7 +1003,9 @@ ${mclass.length ? "#参考资料\n面向年级:" + mclass.join(",") : ""}
 - 你不能输出错误的知识,如果你实在不清楚,输出“对不起,我不确定”
 - 你不能输出违反伦理的内容`;
                 } else if (this.courseState == 5) {
-                    message = `# 任务
+                    message = `NOTICE Language: ${this.getLang()}
+
+# 任务
 请根据<任务教案>,为教师生成本的教学ppt的大纲,大纲包含各环节的步骤,学科知识点。
 
 # 参考资料
@@ -1044,7 +1069,7 @@ ${mclass.length ? "面向年级:" + mclass.join(",") : ""}
                     type == 1
                         ? "6063369f-289a-11ef-8bf4-12e77c4cb76b"
                         : "f8e1ebb2-2e0d-11ef-8bf4-12e77c4cb76b",
-                message: [{ type: "text", text: message.replaceAll('\n', " ") }],
+                message: [{ type: "text", text:this.languageSetting == 1 ? converter2(message.replaceAll('\n', " ")) : message.replaceAll('\n', " ")  }],
                 session_name: uuidv4(),
                 userId: this.userid,
                 file_ids: fileid.length ? [...fileid] : "",

+ 7 - 0
src/components/pages/aiAddCourse/evaList.vue

@@ -206,8 +206,15 @@ export default {
         openAiDialog(type, index) {
             if (type == 'elist') {
                 this.aiElist();
+                if(type == 1){
+                    this.$emit('addCourseBehavior','courseBehavior', `右键学历案-任务${this.itemTaskIndex + 1}-工具${this.toolIndex + 1}-评价设置-${(this.eList && this.eList.length) ? '重新生成评价' : '生成评价'}按钮`)
+                }else{
+                    this.$emit('addCourseBehavior','courseBehavior', `右键学历案-任务${this.itemTaskIndex + 1}-任务${this.toolIndex + 1}-评价设置-${(this.eList && this.eList.length) ? '重新生成评价' : '生成评价'}按钮`)
+                }
+                this.$emit('')
             } else if (type == 'rule') {
                 this.aiRule(index);
+                this.$emit('addCourseBehavior','courseBehavior', `右键学历案-任务${this.itemTaskIndex + 1}-任务${this.toolIndex + 1}-评价设置-评价${index + 1}-重新生成细则按钮`)
             }
         },
         aiElist() {

+ 8 - 0
src/components/pages/classroomObservation/components/addNewAnalysisDialog.vue

@@ -102,6 +102,7 @@
                 />
                 <div class="a-d-b-i-t-title">{{ item.name }}</div>
               </div>
+							<div class="a_d_b_i_author" v-if="item.username">创作者:{{ item.username }}</div>
               <div class="a-d-b-i-bottom">{{ item.detail }}</div>
               <div class="a-d-b-i-bottomPer" style="display: block">
                 {{ item.count }}人已使用
@@ -1349,4 +1350,11 @@ export default {
 .a_d_b_i_t_t_editDialog > div > span {
   margin: 0 5px 0 10px;
 }
+
+.a_d_b_i_author{
+	font-size: 14px;
+	margin-bottom: 5px;
+	margin-top: -5px;
+	font-weight: normal;
+}
 </style>

+ 352 - 53
src/components/pages/classroomObservation/components/analysis.vue

@@ -5,17 +5,23 @@
         <span :class="['a-h-l-icon', showItem ? 'a-h-l-showIcon' : '']"></span>
         <span class="a-h-l-title" v-if="!editTitle">{{ title }}</span>
         <div class="a_h_l_t_input" v-if="editTitle" @click.stop="() => {}">
-          <el-form ref="form" :model="form" :rules="rules" label-position="top" @submit.native.prevent>
+          <el-form
+            ref="form"
+            :model="form"
+            :rules="rules"
+            label-position="top"
+            @submit.native.prevent
+          >
             <el-form-item prop="name">
               <el-input
                 v-model="form.name"
-               	@blur="editNameCheckFn()"
-								@keyup.enter.native="editNameCheckFn"
+                @blur="editNameCheckFn()"
+                @keyup.enter.native="editNameCheckFn"
                 ref="editNameInputRef"
                 placeholder="请输入新的名称"
               ></el-input>
             </el-form-item>
-						<!--   @blur="editNameCheckFn()" -->
+            <!--   @blur="editNameCheckFn()" -->
           </el-form>
         </div>
         <el-tooltip
@@ -39,11 +45,17 @@
         <div
           class="a_h_r_more"
           slot="reference"
+					v-if="!isDrag"
           @click.stop="visible = !visible"
         >
           <img src="@/assets/icon/classroomObservation/moreIcon.svg" alt="" />
         </div>
 
+				<div class="a_h_r_scBtn" v-if="isDrag">
+					<span class="a_h_r_cancel" @click.stop="dragCancel()">取消</span>
+					<span class="a_h_r_submit" @click.stop="dragSubmit()">完成</span>
+				</div>
+
         <div
           class="a_h_r_boxList"
           v-if="visible"
@@ -82,16 +94,199 @@
 
             添加模块
           </div>
-					<div @click="addNewAnalysisGroup(0)"><svg t="1727073223211" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3257" width="200" height="200"><path d="M546.901333 314.282667a46.805333 46.805333 0 0 0-69.802666 0l-296.021334 340.736c-24.32 28.032-3.328 70.314667 34.944 70.314666h591.957334c38.272 0 59.306667-42.282667 34.944-70.314666l-296.021334-340.736z" p-id="3258"></path></svg>添加分析</div>
-					<div @click="addNewAnalysisGroup(1)"><svg style="transform: rotate(180deg);" t="1727073223211" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3257" width="200" height="200"><path d="M546.901333 314.282667a46.805333 46.805333 0 0 0-69.802666 0l-296.021334 340.736c-24.32 28.032-3.328 70.314667 34.944 70.314666h591.957334c38.272 0 59.306667-42.282667 34.944-70.314666l-296.021334-340.736z" p-id="3258"></path></svg>添加分析</div>
-					<div @click="deleteAnalysisGroup()"><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="M6.25 3.125C6.25 2.77982 6.52982 2.5 6.875 2.5H13.125C13.4702 2.5 13.75 2.77982 13.75 3.125C13.75 3.47018 13.4702 3.75 13.125 3.75H6.875C6.52982 3.75 6.25 3.47018 6.25 3.125Z" fill="black" fill-opacity="0.6"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M2.5 5.625C2.5 5.26481 2.77982 5 3.125 5H16.875C17.2202 5 17.5 5.26481 17.5 5.625C17.5 6.03952 17.2202 6.25 16.875 6.25L15.625 6.30435V17.5H4.375V6.25H3.125C2.77982 6.25 2.5 6.03952 2.5 5.625ZM5.625 6.25V16.1957L14.375 16.25V6.30435L5.625 6.25Z" fill="black" fill-opacity="0.6"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M8.125 8.06838C8.47018 8.06838 8.75 8.32276 8.75 8.63656V13.7502C8.75 14.064 8.47018 14.3184 8.125 14.3184C7.77982 14.3184 7.5 14.064 7.5 13.7502V8.63656C7.5 8.32276 7.77982 8.06838 8.125 8.06838Z" fill="black" fill-opacity="0.6"/>
-<path fill-rule="evenodd" clip-rule="evenodd" d="M11.875 8.06838C12.2202 8.06838 12.5 8.32276 12.5 8.63656V13.7502C12.5 14.064 12.2202 14.3184 11.875 14.3184C11.5298 14.3184 11.25 14.064 11.25 13.7502V8.63656C11.25 8.32276 11.5298 8.06838 11.875 8.06838Z" fill="black" fill-opacity="0.6"/>
-</svg>
-删除分析</div>
-				</div>
+					<div @click="moveAnalysis()" v-if="tid">
+						<svg t="1727591669869" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4289" width="200" height="200"><path d="M514.64 511m-84.76 0a84.76 84.76 0 1 0 169.52 0 84.76 84.76 0 1 0-169.52 0Z" fill="#000000" p-id="4290"></path><path d="M661.36 190.85L548.54 78a47.85 47.85 0 0 0-32-14c-1.21-0.09-2.43-0.15-3.66-0.15A48.95 48.95 0 0 0 478.58 78L365.77 190.85a48 48 0 0 0 0 67.88 48 48 0 0 0 67.88 0l31.21-31.21v115.22a48 48 0 0 0 48 48 48 48 0 0 0 48-48V226.11l32.61 32.62a48 48 0 0 0 67.89 0 48 48 0 0 0 0-67.88zM364.23 832.86l112.82 112.81a47.82 47.82 0 0 0 32 14c1.21 0.09 2.43 0.15 3.66 0.15A48.9 48.9 0 0 0 547 945.67l112.83-112.81a48 48 0 0 0 0-67.88 48 48 0 0 0-67.89 0l-31.21 31.2V681a48 48 0 0 0-48-48 48 48 0 0 0-48 48v116.59L432.12 765a48 48 0 0 0-67.89 0 48 48 0 0 0 0 67.86zM831.12 663.76L943.93 551a47.86 47.86 0 0 0 14-32c0.09-1.21 0.15-2.43 0.15-3.67A48.86 48.86 0 0 0 943.93 481L831.12 368.17a48 48 0 0 0-67.89 0 48 48 0 0 0 0 67.88l31.21 31.21H679.23a48 48 0 0 0-48 48 48 48 0 0 0 48 48h116.62l-32.62 32.62a48 48 0 0 0 0 67.88 48 48 0 0 0 67.89 0zM187.88 366.64L75.06 479.45a47.82 47.82 0 0 0-14 32c-0.1 1.21-0.16 2.43-0.16 3.67a48.86 48.86 0 0 0 14.17 34.27l112.81 112.84a48 48 0 0 0 67.88 0 48 48 0 0 0 0-67.88l-31.21-31.21h115.21a48 48 0 0 0 48-48 48 48 0 0 0-48-48H223.14l32.62-32.62a48 48 0 0 0 0-67.88 48 48 0 0 0-67.88 0z" fill="#000000" p-id="4291"></path></svg>
+						移动模块
+					</div>
+          <div @click="addNewAnalysisGroup(0)">
+            <svg
+              t="1727073223211"
+              class="icon"
+              viewBox="0 0 1024 1024"
+              version="1.1"
+              xmlns="http://www.w3.org/2000/svg"
+              p-id="3257"
+              width="200"
+              height="200"
+            >
+              <path
+                d="M546.901333 314.282667a46.805333 46.805333 0 0 0-69.802666 0l-296.021334 340.736c-24.32 28.032-3.328 70.314667 34.944 70.314666h591.957334c38.272 0 59.306667-42.282667 34.944-70.314666l-296.021334-340.736z"
+                p-id="3258"
+              ></path></svg
+            >添加分析
+          </div>
+          <div @click="addNewAnalysisGroup(1)">
+            <svg
+              style="transform: rotate(180deg);"
+              t="1727073223211"
+              class="icon"
+              viewBox="0 0 1024 1024"
+              version="1.1"
+              xmlns="http://www.w3.org/2000/svg"
+              p-id="3257"
+              width="200"
+              height="200"
+            >
+              <path
+                d="M546.901333 314.282667a46.805333 46.805333 0 0 0-69.802666 0l-296.021334 340.736c-24.32 28.032-3.328 70.314667 34.944 70.314666h591.957334c38.272 0 59.306667-42.282667 34.944-70.314666l-296.021334-340.736z"
+                p-id="3258"
+              ></path></svg
+            >添加分析
+          </div>
+
+          <div @click="setTheLocation(0)" v-if="![0].includes(index)">
+            <svg
+              width="16"
+              height="16"
+              viewBox="0 0 16 16"
+              fill="none"
+              xmlns="http://www.w3.org/2000/svg"
+            >
+              <g clip-path="url(#clip0_1551_2752)">
+                <path
+                  d="M11 7.5L8 4L5 7.5H7.47826V13.5C7.47826 13.7761 7.71185 14 8 14C8.28815 14 8.52174 13.7761 8.52174 13.5V7.5H11Z"
+                  fill="black"
+                  fill-opacity="0.9"
+                />
+                <path
+                  fill-rule="evenodd"
+                  clip-rule="evenodd"
+                  d="M2 3L14 3V2L2 2L2 3Z"
+                  fill="black"
+                  fill-opacity="0.9"
+                />
+              </g>
+              <defs>
+                <clipPath id="clip0_1551_2752">
+                  <rect width="16" height="16" fill="white" />
+                </clipPath>
+              </defs>
+            </svg>
+            置顶
+          </div>
+          <div @click="setTheLocation(1)" v-if="![maxIndex].includes(index)">
+            <svg
+              width="16"
+              height="16"
+              style="transform: rotate(180deg);"
+              viewBox="0 0 16 16"
+              fill="none"
+              xmlns="http://www.w3.org/2000/svg"
+            >
+              <g clip-path="url(#clip0_1551_2752)">
+                <path
+                  d="M11 7.5L8 4L5 7.5H7.47826V13.5C7.47826 13.7761 7.71185 14 8 14C8.28815 14 8.52174 13.7761 8.52174 13.5V7.5H11Z"
+                  fill="black"
+                  fill-opacity="0.9"
+                />
+                <path
+                  fill-rule="evenodd"
+                  clip-rule="evenodd"
+                  d="M2 3L14 3V2L2 2L2 3Z"
+                  fill="black"
+                  fill-opacity="0.9"
+                />
+              </g>
+              <defs>
+                <clipPath id="clip0_1551_2752">
+                  <rect width="16" height="16" fill="white" />
+                </clipPath>
+              </defs>
+            </svg>
+            置底
+          </div>
+
+          <div @click="setTheLocation(2)" v-if="![0].includes(index)">
+            <svg
+              width="16"
+              height="16"
+              viewBox="0 0 16 16"
+              fill="none"
+              xmlns="http://www.w3.org/2000/svg"
+            >
+              <g clip-path="url(#clip0_1552_2795)">
+                <path
+                  d="M11 5.5L8 2L5 5.5H7.47826V13.5C7.47826 13.7761 7.71185 14 8 14C8.28815 14 8.52174 13.7761 8.52174 13.5V5.5H11Z"
+                  fill="black"
+                  fill-opacity="0.9"
+                />
+              </g>
+              <defs>
+                <clipPath id="clip0_1552_2795">
+                  <rect width="16" height="16" fill="white" />
+                </clipPath>
+              </defs>
+            </svg>
+            上移
+          </div>
+
+          <div @click="setTheLocation(3)" v-if="![maxIndex].includes(index)">
+            <svg
+              style="transform: rotate(180deg);"
+              width="16"
+              height="16"
+              viewBox="0 0 16 16"
+              fill="none"
+              xmlns="http://www.w3.org/2000/svg"
+            >
+              <g clip-path="url(#clip0_1552_2795)">
+                <path
+                  d="M11 5.5L8 2L5 5.5H7.47826V13.5C7.47826 13.7761 7.71185 14 8 14C8.28815 14 8.52174 13.7761 8.52174 13.5V5.5H11Z"
+                  fill="black"
+                  fill-opacity="0.9"
+                />
+              </g>
+              <defs>
+                <clipPath id="clip0_1552_2795">
+                  <rect width="16" height="16" fill="white" />
+                </clipPath>
+              </defs>
+            </svg>
+            下移
+          </div>
+
+          <div @click="deleteAnalysisGroup()" v-if="maxIndex!==0">
+            <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="M6.25 3.125C6.25 2.77982 6.52982 2.5 6.875 2.5H13.125C13.4702 2.5 13.75 2.77982 13.75 3.125C13.75 3.47018 13.4702 3.75 13.125 3.75H6.875C6.52982 3.75 6.25 3.47018 6.25 3.125Z"
+                fill="black"
+                fill-opacity="0.6"
+              />
+              <path
+                fill-rule="evenodd"
+                clip-rule="evenodd"
+                d="M2.5 5.625C2.5 5.26481 2.77982 5 3.125 5H16.875C17.2202 5 17.5 5.26481 17.5 5.625C17.5 6.03952 17.2202 6.25 16.875 6.25L15.625 6.30435V17.5H4.375V6.25H3.125C2.77982 6.25 2.5 6.03952 2.5 5.625ZM5.625 6.25V16.1957L14.375 16.25V6.30435L5.625 6.25Z"
+                fill="black"
+                fill-opacity="0.6"
+              />
+              <path
+                fill-rule="evenodd"
+                clip-rule="evenodd"
+                d="M8.125 8.06838C8.47018 8.06838 8.75 8.32276 8.75 8.63656V13.7502C8.75 14.064 8.47018 14.3184 8.125 14.3184C7.77982 14.3184 7.5 14.064 7.5 13.7502V8.63656C7.5 8.32276 7.77982 8.06838 8.125 8.06838Z"
+                fill="black"
+                fill-opacity="0.6"
+              />
+              <path
+                fill-rule="evenodd"
+                clip-rule="evenodd"
+                d="M11.875 8.06838C12.2202 8.06838 12.5 8.32276 12.5 8.63656V13.7502C12.5 14.064 12.2202 14.3184 11.875 14.3184C11.5298 14.3184 11.25 14.064 11.25 13.7502V8.63656C11.25 8.32276 11.5298 8.06838 11.875 8.06838Z"
+                fill="black"
+                fill-opacity="0.6"
+              />
+            </svg>
+            删除分析
+          </div>
+
+					
+        </div>
         <!-- </el-popover> -->
         <!-- <div class="a-h-r-btn" @click.stop="addTemplate">
           <img src="@/assets/icon/classroomObservation/newcon.svg" alt="" />
@@ -99,56 +294,69 @@
         </div> -->
       </div>
     </div>
-    <div class="a-main" v-show="showItem">
+    <div class="a-main" v-show="showItem || isDrag">
       <template v-for="(item, index) in analysisItemList">
+				<div class="a_m_dragBox" ref="dragBoxRefTop" :type="`${type}_${index}_${item.tIndex}_0`" v-if="isDrag && index===0"></div>
         <analysisItem
           ref="analysisItemRef"
           v-if="
-            converter(item.jsonData.name) != converter('词频词汇分析') &&(
               ![
                 converter('S-T分析:课堂时间分配'),
                 converter('S-T分析:师生互动分析'),
-                converter('S-T分析:教学模式分析'),
-              ].includes(converter(item.jsonData.name)) && !['bfe844b1-7a45-11ef-9b30-005056b86db5'].includes(item.jsonData.mId)
-			)
+                converter('S-T分析:教学模式分析')
+              ].includes(converter(item.jsonData.name)) &&
+              !['bfe844b1-7a45-11ef-9b30-005056b86db5'].includes(
+                item.jsonData.mId
+              )
           "
           :dialogTagDataList="dialogTagDataList"
-					:dataList="dataList"
+          :dataList="dataList"
           :bmData="bmData"
-          :key="item.id + item.Type + '-'+index"
+          :key="item.id + item.Type + '-' + index"
           :data="item"
           :tid="tid"
+					:dialogTagList="dialogTagList"
           :fileId="fileId"
           :index="index"
           :showBrief="showBrief"
+					:isDrag="isDrag"
           @delItem="delItem"
           @editItem="editItem"
           @saveItem="saveItem"
+					@moveAnalysis="moveAnalysisSubmit"
         />
         <analysisSpecialItem
           v-if="
             [
               converter('S-T分析:课堂时间分配'),
               converter('S-T分析:师生互动分析'),
-              converter('S-T分析:教学模式分析'),
-            ].includes(converter(item.jsonData.name)) || ['bfe844b1-7a45-11ef-9b30-005056b86db5'].includes(item.jsonData.mId)
+              converter('S-T分析:教学模式分析')
+            ].includes(converter(item.jsonData.name)) ||
+              ['bfe844b1-7a45-11ef-9b30-005056b86db5'].includes(
+                item.jsonData.mId
+              )
           "
           :dialogTagDataList="dialogTagDataList"
           ref="analysisItemRef"
           :bmData="bmData"
-          :key="item.id + item.Type+'-' + index"
+          :key="item.id + item.Type + '-' + index"
           :data="item"
           :tid="tid"
+					:dialogTagList="dialogTagList"
           :fileId="fileId"
           :index="index"
+					:isDrag="isDrag"
           :showBrief="showBrief"
           @delItem="delItem"
           @editItem="editItem"
           @saveItem="saveItem"
+					@moveAnalysis="moveAnalysisSubmit"
         />
+				<div class="a_m_dragBox" ref="dragBoxRefBottom" :type="`${type}_${index}_${item.tIndex}_1`" v-if="isDrag"></div>
       </template>
       <div class="a_m_empty" v-if="analysisItemList.length == 0">
-        暂无模块...
+        <span v-if="!isDrag">暂无模块...</span>
+				<div class="a_m_dragBox" ref="dragBoxRefBottom" :type="`${type}_0_0_2`" v-if="isDrag"></div>
       </div>
     </div>
     <editNameDialog ref="editNameDialogRef" @success="editNameSuccess" />
@@ -229,7 +437,25 @@ export default {
       type: String,
       require: true
     },
-		dataList:{
+    dataList: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    },
+    index: {
+      type: Number,
+      default: 0
+    },
+    maxIndex: {
+      type: Number,
+      default: 0
+    },
+		isDrag:{
+			type:Boolean,
+			default:false
+		},
+		dialogTagList:{
 			type:Array,
 			default:()=>{return []}
 		},
@@ -244,7 +470,7 @@ export default {
       return function(word) {
         return converter2(word);
       };
-    }
+    },
   },
   data() {
     return {
@@ -311,7 +537,7 @@ export default {
       // this.$refs.editNameDialogRef.open(this.title)
     },
     editNameCheckFn() {
-			if(!this.editTitle)return;
+      if (!this.editTitle) return;
       this.$refs["form"].validate(valid => {
         if (valid) {
           this.editTitle = false;
@@ -319,7 +545,7 @@ export default {
         } else {
           this.$nextTick(() => {
             this.$refs.editNameInputRef.focus();
-						this.$message.error("该名称不符合规则")
+            this.$message.error("该名称不符合规则");
           });
         }
       });
@@ -328,31 +554,55 @@ export default {
       this.$emit("changeAnalysisName", { name: name, type: this.type });
       // this.$refs.editNameDialogRef.close()
     },
-		addNewAnalysisGroup(po=1){//0:上面   1:下面
-			if (!this.tid) return this.$message.error("请新建课堂,或选择历史课堂");
-			this.visible = false;
-			this.$confirm("确定添加新的分析分组吗?", "添加分析", {
+    addNewAnalysisGroup(po = 1) {
+      //0:上面   1:下面
+      if (!this.tid) return this.$message.error("请新建课堂,或选择历史课堂");
+      this.visible = false;
+      this.$confirm("确定添加新的分析分组吗?", "添加分析", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
-        type: "waring",
+        type: "waring"
       })
         .then(() => {
-					this.$emit("addNewAnalysisGroup",{type:this.type,po:po})
-			}).catch(()=>{})
-		},
-		deleteAnalysisGroup(){
-			if (!this.tid) return this.$message.error("请新建课堂,或选择历史课堂");
-			this.visible = false;
-			this.$confirm("删除后无法恢复,确定删除该分析吗?", "删除分析", {
+          this.$emit("addNewAnalysisGroup", { type: this.type, po: po });
+        })
+        .catch(() => {});
+    },
+    deleteAnalysisGroup() {
+      if (!this.tid) return this.$message.error("请新建课堂,或选择历史课堂");
+      this.visible = false;
+      this.$confirm("删除后无法恢复,确定删除该分析吗?", "删除分析", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
-        type: "waring",
+        type: "waring"
       })
-        .then((res) => {
-					let _groupId = this.analysisItemList.map(i=>i.id)
-					this.$emit("delAnalysisGroup",{type:this.type,groupId:_groupId})
-				}).catch(e=>{})
-			
+        .then(res => {
+          let _groupId = this.analysisItemList.map(i => i.id);
+          this.$emit("delAnalysisGroup", {
+            type: this.type,
+            groupId: _groupId
+          });
+        })
+        .catch(e => {});
+    },
+    setTheLocation(type = 0) {
+      this.visible = false;
+      if (!this.tid) return this.$message.error("请新建课堂,或选择历史课堂");
+      this.$emit("setTheLocation", { value: this.type, type: type });
+    },
+		moveAnalysis(){
+			this.visible = false;
+			this.$parent.isDrag = true;
+		},
+		dragSubmit(){
+			this.visible = false;
+			this.$parent.moveAnalysisSubmit()
+		},
+		moveAnalysisSubmit(data){
+			this.$emit('moveAnalysis',data)
+		},
+		dragCancel(){
+			this.$parent.moveAnalysisCancel()
 		}
   }
 };
@@ -763,19 +1013,68 @@ export default {
   height: 100%;
 }
 .a_h_l_t_input {
-	width: 25%;
-	min-width: 220px;
+  width: 25%;
+  min-width: 220px;
   height: 100%;
   display: flex;
   align-items: center;
 }
 
-.a_h_l_t_input>>>.el-form{
+.a_h_l_t_input >>> .el-form {
+  width: 100%;
+  min-width: 220px;
+}
+
+.a_h_l_t_input >>> .el-form-item {
+  margin-bottom: 0;
+}
+
+.a_m_dragBox{
 	width: 100%;
-	min-width: 220px;
+	height: 40px;
+	border-radius: 3px;
+	box-sizing: border-box;
+	margin-bottom: 10px;
+	border: 1px dashed rgba(54, 129, 252, 1);
+	background-color: #F8F9FA;
+	transition: .3s;
+	cursor: pointer;
+}
+
+.a_h_r_scBtn{
+	height: 100%;
+	padding: 0 10px 0;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+
+.a_h_r_scBtn>.a_h_r_submit{
+	padding: 5px 10px;
+	background-color: rgba(54, 129, 252, 1);
+	border-radius: 3px;
+	color: #fff;
+	cursor: pointer;
+	font-size: 14px;
+}
+
+.a_h_r_submit>a_h_r_submit:hover{
+	background-color: rgb(19, 106, 247);
+}
+
+.a_h_r_scBtn>.a_h_r_cancel{
+	padding: 5px 10px;
+	background-color: #fff;
+	border-radius: 3px;
+	color: black;
+	cursor: pointer;
+	border: solid 1px #e7e7e7;
+	font-size: 14px;
+	margin-right: 10px;
 }
 
-.a_h_l_t_input>>>.el-form-item{
-	margin-bottom: 0;
+.a_h_r_submit>.a_h_r_cancel:hover{
+	background-color: #e0d9d9;
 }
+
 </style>

+ 3 - 3
src/components/pages/classroomObservation/components/analysis2.vue

@@ -2,7 +2,7 @@
 	<div class="analysis">
 		<div class="a-header">
 			<div class="a-h-left" @click.stop="changeShowItem(!showItem)">
-				<span :class="['a-h-l-icon', showItem ? 'a-h-l-showIcon' : '']"></span>
+				<!-- <span :class="['a-h-l-icon', showItem ? 'a-h-l-showIcon' : '']"></span> -->
 				<span class="a-h-l-title">{{ title }}</span>
 			</div>
 			<div class="a-h-right" v-if="showAdd">
@@ -85,8 +85,8 @@ export default {
 	padding: 5px 8px 5px 8px;
 	gap: 8px;
 	opacity: 0px;
-	margin: 20px 0;
-	margin-bottom: 10px;
+	margin: 20px 0px 5px 0;
+	margin-bottom: 5px;
 	border-bottom: 1px #ccc solid;
 }
 .analysis {

+ 176 - 33
src/components/pages/classroomObservation/components/analysisItem.vue

@@ -1,6 +1,15 @@
 <template>
-  <div class="analysisItem">
-    <div class="ai-header" v-show="data.jsonData.name != '词频词汇分析'">
+  <div
+    class="analysisItem"
+    ref="analysisItemRef"
+    :style="
+      `top:${moveTop}px;transition:${isDragging ? '0' : '.3s'}s;${
+        isDragging ? 'z-index:999' : ''
+      }`
+    "
+		@mousedown="moveDown($event)"
+  >
+    <div class="ai-header">
       <div class="ai-h-left" @click="changeOpenItem(!openItem)">
         <span
           :class="['ai-h-l-icon', openItem ? 'ai-h-l-iconActive' : '']"
@@ -37,7 +46,7 @@
           content="修改名称"
           placement="top"
         >
-          <span class="a_h_l_edit" @click.stop="editTitleFn()">
+          <span class="a_h_l_edit" @click.stop="editTitleFn()" v-if="!isDrag">
             <img
               src="../../../../assets/icon/classroomObservation/editIcon2.svg"
             />
@@ -45,10 +54,10 @@
         </el-tooltip>
       </div>
       <div class="ai-h-right">
-        <span style="width: 100px;" class="generateError" v-if="loadNum == 2"
+        <span style="width: 100px;" class="generateError" v-if="loadNum == 2&& !isDrag"
           >优化失败
         </span>
-        <span style="width: 100px" v-if="loadNum == 1">
+        <span style="width: 100px" v-if="loadNum == 1 && !isDrag">
           <span v-if="loading" class="generate">
             <img
               :src="
@@ -66,8 +75,9 @@
             生成完成
           </span>
         </span>
+
         <span
-          v-if="loadNum == 0 && !openItem && tid"
+          v-if="loadNum == 0 && !openItem && tid && !isDrag"
           class="ai-h-r-icon4"
           @click="delBtn()"
         >
@@ -86,7 +96,7 @@
         </span>
 
         <span
-          v-if="loadNum == 0 && openItem"
+          v-if="loadNum == 0 && openItem && !isDrag"
           :class="['ai-h-r-icon1', showIndex <= 0 ? 'ai_h_r_iconOpacity' : '']"
           @click="changeShowIndex(-1)"
         >
@@ -104,8 +114,9 @@
           </el-tooltip>
         </span>
 
+
         <span
-          v-if="loadNum != 1 && openItem"
+          v-if="loadNum != 1 && openItem && !isDrag"
           :class="[
             showIndex >= historyResult.length - 1 ? 'ai_h_r_iconOpacity' : ''
           ]"
@@ -125,7 +136,7 @@
           </el-tooltip>
         </span>
 
-        <span v-if="loadNum != 1 && openItem && tid" @click="editBtn()">
+        <span v-if="loadNum != 1 && openItem && tid && !isDrag" @click="editBtn()">
           <el-tooltip
             class="item"
             effect="light"
@@ -145,7 +156,7 @@
             loadNum != 1 &&
               openItem &&
               tid &&
-              ['1', '2', '3'].includes(data.jsonData.echartsType)
+              ['1', '2', '3'].includes(data.jsonData.echartsType) && !isDrag
           "
           @click="editEcharts()"
         >
@@ -166,7 +177,7 @@
         <!-- <span class="ai-h-r-icon4" @click.stop="delBtn()"></span> -->
       </div>
     </div>
-    <div class="ai-main" v-if="openItem">
+    <div class="ai-main" v-if="openItem && !isDrag">
       <div class="a-m-brief" v-if="showBrief">
         {{ data.jsonData.result }}
         <!-- <mdView :text="data.jsonData.result" /> -->
@@ -257,10 +268,22 @@ export default {
         return {};
       }
     },
-		dataList:{
-			type:Array,
-			default:()=>{return []}
-		},
+    dataList: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    },
+    isDrag: {
+      type: Boolean,
+      default: false
+    },
+    dialogTagList: {
+      type: Array,
+      default: () => {
+        return [];
+      }
+    }
   },
   data() {
     return {
@@ -296,7 +319,13 @@ export default {
             message: "长度需在1-20个字符之间"
           }
         ]
-      }
+      },
+      startY: 0,
+      startTop: 0,
+      moveTop: 0,
+      isDragging: false,
+      dragBoxList: [],
+      enterDrag: null
     };
   },
   computed: {
@@ -316,6 +345,7 @@ export default {
   },
   methods: {
     changeOpenItem(newValue) {
+			if(this.isDrag)return;
       if (this.loading == true && this.loadNum != 0)
         return this.$message("请稍后...");
       this.loadNum = 0;
@@ -327,7 +357,7 @@ export default {
         this.openItem = false;
         this.loadNum = 1;
         let type = 0; //0 用agentId  1:用提示词 3:啥都没有
-				let tips = "";
+        let tips = "";
         let assistant =
           this.dialogTagDataList.find(i => i.id == this.data.jsonData.mId) ||
           this.dialogTagDataList.find(i => i.name == this.data.jsonData.name);
@@ -343,13 +373,15 @@ export default {
 
         if (assistant.tips) {
           type = 1;
-					let analysisData = ``;
-					for(let i = 0;i<this.dataList.length;i++){
-							let _json = this.dataList[i].jsonData;
-							analysisData +=`### ${_json.anotherName?_json.anotherName:_json.name}\n`
-							analysisData +=`${_json.content}\n`
-					}
-					tips = assistant.tips.replaceAll('${analysisData}',analysisData)
+          let analysisData = ``;
+          for (let i = 0; i < this.dataList.length; i++) {
+            let _json = this.dataList[i].jsonData;
+            analysisData += `### ${
+              _json.anotherName ? _json.anotherName : _json.name
+            }\n`;
+            analysisData += `${_json.content}\n`;
+          }
+          tips = assistant.tips.replaceAll("${analysisData}", analysisData);
         } else if (assistant.agentid) {
           type = 0;
         } else {
@@ -364,8 +396,6 @@ export default {
 课堂名称:${this.bmData.courseName}  搜课年级:${this.bmData.grade}  授课科目:${this.bmData.subject}`;
         }
 
-
-
         // console.log('👇')
         // return console.log(_msg)
         let parm = {
@@ -425,7 +455,8 @@ export default {
             // }
             let _copyData = JSON.parse(JSON.stringify(this.data));
             // _copyData.jsonData.result = "";
-						if(!_copyData.jsonData.mId && assistant.agentid)_copyData.jsonData.mId = assistant.agentid
+            if (!_copyData.jsonData.mId && assistant.agentid)
+              _copyData.jsonData.mId = assistant.agentid;
             _copyData.jsonData.content = _data.message;
             _copyData.jsonData.dataFileList = [];
             _copyData.jsonData.fileList = [];
@@ -659,7 +690,7 @@ export default {
             res.forEach((item, index) => {
               if (index == 0) return; //去掉表头
               radarData.push({ name: item[0], max: 5 });
-							let _valueItem = item[1] ? item[1] :"0";
+              let _valueItem = item[1] ? item[1] : "0";
               let _value = _valueItem.match(/(\d+)/);
               _value = _value ? parseInt(_value[0]) : 0;
               seriesData.value.push(_value);
@@ -716,7 +747,7 @@ export default {
             stepList = this.calculateTopValues(res.length - 1);
             res.forEach((item, index) => {
               if (index == 0) return;
-							let _valueItem = item[1] ? item[1] : "0";
+              let _valueItem = item[1] ? item[1] : "0";
               let _value = _valueItem.match(/(\d+)/);
               _value = _value ? parseInt(_value[0]) : 0;
               // 求百分比
@@ -816,7 +847,7 @@ export default {
       }
       this.loadNum = 2;
       this.loading = false;
-			return this.$message.error("生成图表失败");
+      return this.$message.error("生成图表失败");
     },
     calculateTopValues(len, minTop = -80, maxTop = 70, maxStep = 40) {
       const length = len;
@@ -864,9 +895,9 @@ export default {
 
           // 匹配每个单元格 (th 或 td)
           while ((cellMatch = cellRegex.exec(rowContent)) !== null) {
-						let _text = cellMatch[2].trim();
-						_text = _text.replace(/&[a-zA-Z]+;/g, '');
-						_text = _text.replace(/<\/?[^>]+(>|$)/g, '')
+            let _text = cellMatch[2].trim();
+            _text = _text.replace(/&[a-zA-Z]+;/g, "");
+            _text = _text.replace(/<\/?[^>]+(>|$)/g, "");
             rowData.push(_text); // 将每个单元格的内容添加到当前行的数组中
           }
 
@@ -890,6 +921,101 @@ export default {
 
       // 组合三种颜色(红、绿、蓝)的随机值
       return `#${randomHex()}${randomHex()}${randomHex()}`;
+    },
+    moveDown(e) {
+			if(!this.isDrag)return;
+      this.isDragging = true;
+      this.startY = e.clientY;
+      this.$nextTick(() => {
+        this.dialogTagList.forEach(i => {
+          this.dragBoxList.push(
+            ...this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
+              .dragBoxRefTop
+          );
+					if(this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs.dragBoxRefBottom.length>0){
+						this.dragBoxList.push(
+            ...this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
+              .dragBoxRefBottom
+          );
+					}else if(this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs.dragBoxRefBottom){
+						this.dragBoxList.push(this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs.dragBoxRefBottom);
+					}
+          
+        });
+        // 禁用页面文本选择
+        document.body.style.userSelect = "none";
+        document.addEventListener("mousemove", this.onMouseMove);
+        document.addEventListener("mouseup", this.stopDragging);
+      });
+    },
+    onMouseMove(e) {
+      if (!this.isDragging) return;
+
+      const newTop = e.clientY - this.startY; // 计算鼠标移动的Y轴距离
+
+      // // 更新div的top样式,使其跟随鼠标移动
+      this.moveTop = newTop;
+      // this.dragBoxList.forEach(i=>{
+      // 	let _i = i.getBoundingClientRect();
+      // 	if(e.clientX>=_i.left && e.clientX<=_i.right && e.clientY>=_i.top && e.clientY<=_i.bottom){
+      // 		this.enterDrag = i;
+      // 		console.log("👇")
+      // 		console.log(e)
+      // 	}else{
+      // 		this.enterDrag = null;
+      // 	}
+      // })
+      // for (let i = 0; i <= this.dragBoxList.length; i++) {
+      //   let _i = this.dragBoxList[i].getBoundingClientRect();
+      //   if (
+      //     e.clientX >= _i.left &&
+      //     e.clientX <= _i.right &&
+      //     e.clientY >= _i.top &&
+      //     e.clientY <= _i.bottom
+      //   ) {
+      //     this.enterDrag = this.dragBoxList[i];
+      //     console.log("👇");
+      //     console.log(e);
+      // 		break;
+      //   } else {
+      //     this.enterDrag = null;
+      //   }
+      // }
+    },
+    stopDragging(e) {
+      this.isDragging = false;
+      try {
+        for (let i = 0; i <= this.dragBoxList.length; i++) {
+          let _i = this.dragBoxList[i].getBoundingClientRect();
+          if (
+            e.clientX >= _i.left &&
+            e.clientX <= _i.right &&
+            e.clientY >= _i.top &&
+            e.clientY <= _i.bottom
+          ) {
+            this.enterDrag = this.dragBoxList[i];
+            break;
+          } else {
+            this.enterDrag = null;
+          }
+        }
+        // 恢复页面的文本选择
+        document.body.style.userSelect = "";
+        if (!this.enterDrag) {
+          this.moveTop = 0;
+        } else {
+					this.moveTop = 0;
+					let moveData = this.enterDrag.getAttribute('type');
+					this.$emit("moveAnalysis",{form:`${this.data.Type}_${this.index}_${this.data.tIndex}`,to:moveData})
+        }
+        // 移除全局的鼠标移动和释放事件
+        document.removeEventListener("mousemove", this.onMouseMove);
+        document.removeEventListener("mouseup", this.stopDragging);
+      } catch (error) {
+				this.moveTop = 0;
+				document.removeEventListener("mousemove", this.onMouseMove);
+        document.removeEventListener("mouseup", this.stopDragging);
+			}
     }
   },
   mounted() {
@@ -913,6 +1039,9 @@ export default {
   border: 1px solid #e7e7e7;
   border-radius: 4px;
   transition: 0.3s;
+  background-color: #fff;
+  position: relative;
+  top: 0;
 }
 
 .analysisItem:hover {
@@ -1018,6 +1147,20 @@ export default {
   display: block;
 }
 
+.ai-h-r-moveIcon {
+  display: none;
+  cursor: move !important;
+}
+
+.analysisItem:hover .ai-h-r-moveIcon {
+  display: block;
+}
+
+.ai-h-r-moveIcon > svg {
+  width: 18px;
+  height: 18px;
+}
+
 .ai-main {
   width: 100%;
   height: auto;

+ 2 - 2
src/components/pages/classroomObservation/components/analysisItem2.vue

@@ -2,9 +2,9 @@
 	<div class="analysisItem">
 		<div class="ai-header" v-show="data.jsonData.name != '词频词汇分析'">
 			<div class="ai-h-left" @click.stop="changeOpenItem(!openItem)">
-				<span
+				<!-- <span
 					:class="['ai-h-l-icon', openItem ? 'ai-h-l-iconActive' : '']"
-				></span>
+				></span> -->
 				<span class="ai-h-l-text">{{ data.jsonData.name }}</span>
 			</div>
 			<div class="ai-h-right" v-if="showAdd">

+ 124 - 6
src/components/pages/classroomObservation/components/analysisSpecialItem.vue

@@ -1,6 +1,11 @@
 <template>
-  <div class="analysisItem">
-    <div class="ai-header" v-show="data.jsonData.name != '词频词汇分析'">
+  <div class="analysisItem" ref="analysisItemRef" :style="
+      `top:${moveTop}px;transition:${isDragging ? '0' : '.3s'}s;${
+        isDragging ? 'z-index:999' : ''
+      }`
+    "
+		@mousedown="moveDown($event)">
+    <div class="ai-header">
       <div class="ai-h-left" @click.stop="changeOpenItem(!openItem)">
         <span
           :class="['ai-h-l-icon', openItem ? 'ai-h-l-iconActive' : '']"
@@ -38,14 +43,14 @@
           content="修改名称"
           placement="top"
         >
-          <span class="a_h_l_edit" @click.stop="editTitleFn()">
+          <span class="a_h_l_edit" @click.stop="editTitleFn()" v-if="!isDrag">
             <img
               src="../../../../assets/icon/classroomObservation/editIcon2.svg"
             />
           </span>
         </el-tooltip>
       </div>
-      <div class="ai-h-right">
+      <div class="ai-h-right" v-if="!isDrag">
         <span style="width: 100px" class="generateError" v-if="loadNum == 2"
           >优化失败
         </span>
@@ -162,7 +167,7 @@
         <!-- <span class="ai-h-r-icon4" @click.stop="delBtn()"></span> -->
       </div>
     </div>
-    <div class="ai-main" v-if="openItem">
+    <div class="ai-main" v-if="openItem && !isDrag">
       <div class="a-m-brief" v-if="showBrief">
         {{ data.jsonData.result }}
       </div>
@@ -266,6 +271,16 @@ export default {
     showBrief: {
       type: Boolean,
       default: true
+    },
+		isDrag: {
+      type: Boolean,
+      default: false
+    },
+    dialogTagList: {
+      type: Array,
+      default: () => {
+        return [];
+      }
     }
   },
   data() {
@@ -303,7 +318,13 @@ export default {
             message: "长度需在1-20个字符之间"
           }
         ]
-      }
+      },
+			startY: 0,
+      startTop: 0,
+      moveTop: 0,
+      isDragging: false,
+      dragBoxList: [],
+      enterDrag: null
     };
   },
   computed: {
@@ -328,6 +349,7 @@ export default {
   },
   methods: {
     changeOpenItem(newValue) {
+			if(this.isDrag)return;
       if (this.loading == true && this.loadNum != 0)
         return this.$message("请稍后...");
       this.loadNum = 0;
@@ -1103,6 +1125,101 @@ CH:${_CH}
 
         resolve(_contentTableList);
       });
+    },
+		moveDown(e) {
+			if(!this.isDrag)return;
+      this.isDragging = true;
+      this.startY = e.clientY;
+      this.$nextTick(() => {
+        this.dialogTagList.forEach(i => {
+          this.dragBoxList.push(
+            ...this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
+              .dragBoxRefTop
+          );
+					if(this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs.dragBoxRefBottom.length>0){
+						this.dragBoxList.push(
+            ...this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs
+              .dragBoxRefBottom
+          );
+					}else if(this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs.dragBoxRefBottom){
+						this.dragBoxList.push(this.$parent.$parent.$refs[`analysis_${i.value}`][0].$refs.dragBoxRefBottom);
+					}
+          
+        });
+        // 禁用页面文本选择
+        document.body.style.userSelect = "none";
+        document.addEventListener("mousemove", this.onMouseMove);
+        document.addEventListener("mouseup", this.stopDragging);
+      });
+    },
+    onMouseMove(e) {
+      if (!this.isDragging) return;
+
+      const newTop = e.clientY - this.startY; // 计算鼠标移动的Y轴距离
+
+      // // 更新div的top样式,使其跟随鼠标移动
+      this.moveTop = newTop;
+      // this.dragBoxList.forEach(i=>{
+      // 	let _i = i.getBoundingClientRect();
+      // 	if(e.clientX>=_i.left && e.clientX<=_i.right && e.clientY>=_i.top && e.clientY<=_i.bottom){
+      // 		this.enterDrag = i;
+      // 		console.log("👇")
+      // 		console.log(e)
+      // 	}else{
+      // 		this.enterDrag = null;
+      // 	}
+      // })
+      // for (let i = 0; i <= this.dragBoxList.length; i++) {
+      //   let _i = this.dragBoxList[i].getBoundingClientRect();
+      //   if (
+      //     e.clientX >= _i.left &&
+      //     e.clientX <= _i.right &&
+      //     e.clientY >= _i.top &&
+      //     e.clientY <= _i.bottom
+      //   ) {
+      //     this.enterDrag = this.dragBoxList[i];
+      //     console.log("👇");
+      //     console.log(e);
+      // 		break;
+      //   } else {
+      //     this.enterDrag = null;
+      //   }
+      // }
+    },
+    stopDragging(e) {
+      this.isDragging = false;
+      try {
+        for (let i = 0; i <= this.dragBoxList.length; i++) {
+          let _i = this.dragBoxList[i].getBoundingClientRect();
+          if (
+            e.clientX >= _i.left &&
+            e.clientX <= _i.right &&
+            e.clientY >= _i.top &&
+            e.clientY <= _i.bottom
+          ) {
+            this.enterDrag = this.dragBoxList[i];
+            break;
+          } else {
+            this.enterDrag = null;
+          }
+        }
+        // 恢复页面的文本选择
+        document.body.style.userSelect = "";
+        if (!this.enterDrag) {
+          this.moveTop = 0;
+        } else {
+					this.moveTop = 0;
+					let moveData = this.enterDrag.getAttribute('type');
+					this.$emit("moveAnalysis",{form:`${this.data.Type}_${this.index}_${this.data.tIndex}`,to:moveData})
+        }
+        // 移除全局的鼠标移动和释放事件
+        document.removeEventListener("mousemove", this.onMouseMove);
+        document.removeEventListener("mouseup", this.stopDragging);
+      } catch (error) {
+				this.moveTop = 0;
+				document.removeEventListener("mousemove", this.onMouseMove);
+        document.removeEventListener("mouseup", this.stopDragging);
+			}
     }
   },
   mounted() {
@@ -1126,6 +1243,7 @@ CH:${_CH}
   border: 1px solid #e7e7e7;
   border-radius: 4px;
   transition: 0.3s;
+	position: relative;
 }
 
 .analysisItem:hover {

+ 1 - 1
src/components/pages/classroomObservation/components/analysisTemplateDialog.vue

@@ -1238,7 +1238,7 @@ export default {
 .ad_h_b_shu {
   width: 1px;
   height: 10px;
-  background-color: #00000066;
+  background-color: #999999;
   margin: 2px 8px 0 8px;
 }
 

+ 3 - 0
src/components/pages/classroomObservation/components/chatArea.vue

@@ -29,6 +29,9 @@
         @startTape="onClickStartRecord"
         @uploadTape="uploadRecording"
         :uploadFileLoading="uploadFileLoading"
+				:teacherVoiceprintList="teacherVoiceprintList"
+				:chosenVoiceprint="chosenVoiceprint"
+				:controlsStatus="controlsStatus"
         ref="startPageRef"
       />
       <!-- 原文速递 -->

+ 150 - 204
src/components/pages/classroomObservation/components/messageArea.vue

@@ -41,8 +41,11 @@
       <analysis
         v-for="(item, index) in dialogTagList"
         :key="item.value"
+        :index="index"
+        :dialogTagList="dialogTagList"
+        :maxIndex="dialogTagList.length - 1"
         @updateMessage="updateMessage"
-        :ref="`analysis${item.value}`"
+        :ref="`analysis_${item.value}`"
         :showBrief="showBrief"
         @delItem="delAnalysisItem"
         @editItem="editAnalysisItem"
@@ -50,6 +53,8 @@
         @changeAnalysisName="changeAnalysisName"
         @addNewAnalysisGroup="addNewAnalysisGroup"
         @delAnalysisGroup="delAnalysisGroup"
+        @setTheLocation="setTheLocation"
+        @moveAnalysis="moveAnalysis"
         :dataList="dataList.filter(i => !(item.value == 0 && i.tIndex == 2))"
         :bmData="bmData.jsonData"
         :title="item.name"
@@ -62,207 +67,10 @@
         :type="item.value"
         :tid="tid"
         :fileId="fileId"
+        :isDrag="isDrag"
         v-loading="item.loading"
       />
-
-      <!-- <div class="ma_m_addNewAnalysisBox"></div> -->
-
-      <!-- <analysis
-				@updateMessage="updateMessage"
-				ref="analysis0"
-				:showBrief="showBrief"
-				@delItem="delAnalysisItem"
-				@editItem="editAnalysisItem"
-				@saveItem="saveAnalysis"
-				:bmData="bmData.jsonData"
-				title="通用课堂分析"
-				:dialogTagDataList="dialogTagDataList"
-				:analysisItemList="dataList.filter((i) => i.Type === 0 && i.tIndex!=2)"
-				:type="0"
-				:tid="tid"
-				:fileId="fileId"
-				v-loading="currencyLoading"
-			/>
-			<analysis
-				@updateMessage="updateMessage"
-				ref="analysis1"
-				:showBrief="showBrief"
-				:bmData="bmData.jsonData"
-				@delItem="delAnalysisItem"
-				@editItem="editAnalysisItem"
-				@saveItem="saveAnalysis"
-				title="学科课堂分析"
-				:dialogTagDataList="dialogTagDataList"
-				:analysisItemList="dataList.filter((i) => i.Type === 1)"
-				:type="1"
-				:tid="tid"
-				:fileId="fileId"
-				v-loading="scienceLoading"
-			/>
-			<analysis
-				@updateMessage="updateMessage"
-				ref="analysis2"
-				:bmData="bmData.jsonData"
-				:showBrief="showBrief"
-				@delItem="delAnalysisItem"
-				@editItem="editAnalysisItem"
-				@saveItem="saveAnalysis"
-				title="扩展分析"
-				:dialogTagDataList="dialogTagDataList"
-				:analysisItemList="dataList.filter((i) => i.Type === 2)"
-				:type="2"
-				:tid="tid"
-				:fileId="fileId"
-				v-loading="extendLoading"
-			/> -->
-      <!-- <analysis
-				@updateMessage="updateMessage"
-				ref="analysis3"
-				:showBrief="showBrief"
-				@delItem="delAnalysisItem"
-				@editItem="editAnalysisItem"
-				title="增值性分析"
-				:dialogTagDataList="dialogTagDataList"
-				:analysisItemList="dataList.filter((i) => i.Type === 3)"
-				:type="3"
-				:tid="tid"
-				:fileId="fileId"
-				v-loading="valueAddedLoading"
-			/> -->
-      <!-- <currencyAnalysis
-				@updateMessage="updateMessage"
-				@delItem="delAnalysisItem"
-				@editItem="editAnalysisItem"
-				:analysisItemList="dataList.filter((i) => i.Type === 0)"
-				:tid="tid"
-				v-loading="currencyLoading"
-				ref="currencyAnalysisRef"
-			/>
-
-			<scienceAnalysis
-				@updateMessage="updateMessage"
-				@delItem="delAnalysisItem"
-				@editItem="editAnalysisItem"
-				:analysisItemList="dataList.filter((i) => i.Type === 1)"
-				:tid="tid"
-				v-loading="scienceLoading"
-				ref="scienceAnalysisRef"
-			/>
-
-			<extendAnalysis
-				@updateMessage="updateMessage"
-				@delItem="delAnalysisItem"
-				@editItem="editAnalysisItem"
-				:analysisItemList="dataList.filter((i) => i.Type === 2)"
-				:tid="tid"
-				v-loading="extendLoading"
-				ref="extendAnalysisRef"
-			/> -->
-      <!-- <div style="height: 10000px;"></div> -->
     </div>
-    <!-- <el-dialog
-			:center="true"
-			:visible.sync="dialogVisible"
-			width="1200px"
-			class="addTemplateDialog"
-		>
-			<div class="a-d-top">
-				<div class="a-d-topTit"><div style="width: 136px">添加模块</div></div>
-				<div>
-					<el-input
-						placeholder="请输入内容"
-						prefix-icon="el-icon-search"
-						v-model="input2"
-						clearable
-					>
-					</el-input>
-				</div>
-				<div class="a-d-t-right">
-					<span @click.stop="dialogVisible = false">×</span>
-				</div>
-			</div>
-			<div style="display: flex; height: 100%">
-				<div class="a-d-t-left">
-					<div
-						:style="
-							tagIndex == index
-								? 'background: rgba(226, 238, 255, 1);color: rgba(54, 129, 252, 1)'
-								: ''
-						"
-						class="a-d-t-l-item"
-						v-for="(item, index) in dialogTagList"
-						:key="item.id"
-						@click.stop="tagIndex = index"
-					>
-						{{ item.name }}
-					</div>
-				</div>
-				<div class="a-d-box">
-					<div
-						style="
-							font-family: PingFang SC;
-							font-size: 16px;
-							font-weight: 600;
-							line-height: 22px;
-							text-align: left;
-							margin: 20px 0;
-							margin-bottom: 10px;
-						"
-					>
-						推荐
-					</div>
-					<div class="a-d-b-subject">
-						<span
-							:class="[
-								'a_d_b_s_btn',
-								tagSubject == item ? 'a_d_b_s_ActiveBtn' : '',
-							]"
-							v-for="(item, index) in tagSubjectList"
-							:key="index + '-' + tagIndex"
-							@click.stop="tagSubject = item"
-							>{{ item }}</span
-						>
-					</div>
-					<div style="display: flex; flex-wrap: wrap">
-						<div
-							class="a-d-b-item"
-							v-for="(item, index) in searchDataList"
-							:key="index"
-						>
-							<div class="a-d-b-i-top">
-								<img
-									style="height: 22px; width: 22px"
-									:src="
-										require('../../../../assets/icon/classroomObservation/digImg.svg')
-									"
-								/>
-								<div class="a-d-b-i-t-title">{{ item.name }}</div>
-							</div>
-							<div class="a-d-b-i-bottom">{{ item.detail }}</div>
-							<div class="a-d-b-i-bottomPer" style="display: block">
-								{{ item.count }}人已使用
-							</div>
-							<div class="a-d-b-i-bottomBtn" style="display: none">
-								<div
-									style="
-										display: flex;
-										width: 100%;
-										justify-content: space-around;
-									"
-								>
-									<div
-										class="a-d-b-i-t-btn1"
-										@click="addAnalysisItem(item.name)"
-									>
-										添加
-									</div>
-								</div>
-							</div>
-						</div>
-					</div>
-				</div>
-			</div>
-		</el-dialog> -->
     <!-- 添加模块 -->
     <addNewAnalysisDialog
       @update="getDialogTagDataList"
@@ -355,7 +163,9 @@ export default {
       dialogTagDataList: [],
       bmData: {},
       dataList: [],
-      imageList: {}
+      imageList: {},
+      isDrag: false,
+      copyDataList: []
     };
   },
   computed: {
@@ -384,6 +194,11 @@ export default {
     // },
   },
   watch: {
+    isDrag(newValue) {
+      if (newValue) {
+        this.copyDataList = JSON.parse(JSON.stringify(this.dataList));
+      }
+    }
     // tagIndex() {
     // 	this.tagSubject = "";
     // },
@@ -557,7 +372,7 @@ export default {
               // } else if (_result.Type == 2) {
               //   this.$refs.analysis2[0].getReport(_result.id);
               // }
-              this.$refs[`analysis${_result.Type}`][0].getReport(_result.id);
+              this.$refs[`analysis_${_result.Type}`][0].getReport(_result.id);
               this.$message.success("添加成功");
               resolve();
             } else {
@@ -923,19 +738,22 @@ export default {
     getData(arr = []) {
       return new Promise(resolve => {
         this.dataList = [];
+        this.isDrag = false;
         this.$forceUpdate();
         if (this.tid) {
           return this.getAnalysisData(0).then(res => {
             this.$nextTick(() => {
               let promises = [];
               this.$nextTick(() => {
-								let forData = arr.length?arr:this.dialogTagList;
-								this.dialogTagList = forData;
+                let forData = arr.length ? arr : this.dialogTagList;
+                this.dialogTagList = forData;
                 forData.forEach(i => {
                   if (i.value === 0) return;
                   promises.push(this.getAnalysisData(i.value));
                 });
                 Promise.all(promises).then(res => {
+                  this.dataList.sort((a, b) => a.tIndex - b.tIndex);
+                  this.dataList.sort((a, b) => a.Type - b.Type);
                   resolve();
                 });
               });
@@ -1160,7 +978,7 @@ export default {
         })
           .then(() => {
             this.dialogTagList.forEach(i => {
-              this.$refs[`analysis${i.value}`][0].getReport();
+              this.$refs[`analysis_${i.value}`][0].getReport();
             });
             // this.$refs["analysis0"][0].getReport();
             // this.$refs["analysis1"][0].getReport();
@@ -1352,6 +1170,133 @@ export default {
         }
       });
     },
+    setTheLocation(data) {
+      let moveList = JSON.parse(JSON.stringify(this.dialogTagList));
+      let _index = moveList.findIndex(i => i.value === data.value);
+      if (_index === -1) return; // 如果没找到,直接退出
+      if (data.type == 0) {
+        // 置顶
+        moveList = this.moveListFn(moveList, _index, 0);
+      } else if (data.type == 1) {
+        // 置底
+        moveList = this.moveListFn(moveList, _index, moveList.length - 1);
+      } else if (data.type == 2) {
+        // 上移
+        moveList = this.moveListFn(moveList, _index, _index - 1);
+      } else if (data.type == 3) {
+        // 下移
+        moveList = this.moveListFn(moveList, _index, _index + 1);
+      }
+      this.dialogTagList = moveList;
+      this.bmData.jsonData["dialogTagList"] = this.dialogTagList;
+      this.saveData(this.bmData);
+      this.$forceUpdate();
+    },
+    moveListFn(arr, fromIndex, toIndex) {
+      // 确保索引在数组范围内
+      if (
+        fromIndex >= 0 &&
+        fromIndex < arr.length &&
+        toIndex >= 0 &&
+        toIndex < arr.length
+      ) {
+        // 删除要移动的元素并存储
+        const element = arr.splice(fromIndex, 1)[0];
+        // 在新的位置插入元素
+        arr.splice(toIndex, 0, element);
+      }
+      return arr;
+    },
+    moveArrayElement(arr, fromIndex, toIndex) {
+      const element = arr[fromIndex];
+      arr.splice(fromIndex, 1); // 删除原位置的元素
+
+      // 调整目标下标以确保正确插入
+      const newIndex = toIndex < fromIndex ? toIndex : toIndex - 1;
+
+      arr.splice(newIndex, 0, element); // 插入到新位置
+      return arr;
+    },
+    moveAnalysis(data) {
+      let _form = data.form.split("_");
+      let _to = data.to.split("_");
+
+      if (_form[0] === _to[0] && _form[1] === _to[1] && _form[2] === _to[2])
+        return console.log("位置不变");
+      if (
+        _form[0] === _to[0] &&
+        parseInt(_form[1]) - 1 === parseInt(_to[1]) &&
+        _to[3] == "1"
+      )
+        return console.log("位置不变2");
+      let _formIndex = this.dataList.findIndex(
+        i => i.Type == parseInt(_form[0]) && i.tIndex == parseInt(_form[2])
+      );
+      let _toIndex = -1;
+      if (["0", "1"].includes(_to[3])) {
+        _toIndex = this.dataList.findIndex(
+          i => i.Type == parseInt(_to[0]) && i.tIndex == parseInt(_to[2])
+        );
+      }
+      // 更新Type与tIndex
+      this.dataList[_formIndex].Type = parseInt(_to[0]);
+      if (_to[3] === "1") {
+        //下面
+        this.moveArrayElement(this.dataList, _formIndex, _toIndex+1);
+      } else if (_to[3] === "0") {
+        //上面
+        this.moveArrayElement(this.dataList, _formIndex, _toIndex);
+      } else if (_to[3] === "2") {
+        console.log("移到了空模块");
+      }
+
+      this.dialogTagList.forEach(i => {
+        let _data = this.dataList.filter(i2 => i2.Type === i.value);
+        let _fTIndex = i.value === 0 ? 2 : 0;
+        _data.forEach(i3 => {
+          if (this.dataList.find(i4 => i4.id === i3.id).tIndex != _fTIndex) {
+            this.dataList.find(i4 => i4.id === i3.id).tIndex = _fTIndex;
+          }
+          _fTIndex++;
+        });
+      });
+      this.$forceUpdate();
+      console.log("更换成功");
+    },
+    moveAnalysisCancel() {
+      this.isDrag = false;
+      this.dataList = JSON.parse(JSON.stringify(this.copyDataList));
+      this.copyDataList = [];
+    },
+		moveAnalysisSubmit(){
+			this.loading = true;
+			if(JSON.stringify(this.copyDataList)===JSON.stringify(this.dataList)){
+				this.isDrag = false;
+				this.loading = false;
+				// this.$message.success("更换成功")
+				return 
+			}
+
+			let _copyData = JSON.parse(JSON.stringify(this.dataList));
+
+			_copyData = _copyData.filter(i=>!(i.Type==0&&i.tIndex==2));
+
+			let _result = _copyData.map(i=>({id:i.id,tIndex:i.tIndex,Type:i.Type}));
+			
+			let params = {
+				data:JSON.stringify(_result)
+			}
+			this.ajax.post('https://gpt4.cocorobo.cn/batch_update_type_tindex',params).then(res=>{
+				this.isDrag = false;
+				this.loading = false;
+				this.$message.success("保存成功")
+			}).catch(e=>{
+				console.log(e)
+				this.isDrag = true;
+				this.loading = false;
+				this.$message.error("保存失败")
+			})
+		},
     init() {
       this.bmData = {};
       this.getDefaultData();
@@ -1418,6 +1363,7 @@ export default {
   width: 100%;
   height: calc(100vh - 123px);
   overflow: auto;
+  position: relative;
 }
 
 .a-d-top {

+ 161 - 36
src/components/pages/classroomObservation/components/startPage.vue

@@ -20,20 +20,52 @@
     </div>
     <div class="sp-main">
       <div class="sp_m_box">
-        <div class="sp_m_b_top"></div>
+        <div class="sp_m_b_top">
+          <div class="sp_m_b_t_voice">
+            <div class="sp_m_b_t_v_title">声纹选择</div>
+            <span>请选择/新增本次课堂授课教师的声纹</span>
+            <div class="sp_m_b_t_v_inputArea">
+              <el-select
+                v-model="inputValue"
+                @change="handleSelect"
+                multiple
+                placeholder="请选择"
+                :multiple-limit="10"
+                filterable
+							 :disabled="controlsStatus === 1"
+              >
+                <el-option
+                  v-for="item in teacherList"
+                  :key="item.id"
+                  :label="item.name"
+                  :value="item.id"
+                >
+                </el-option>
+              </el-select>
+              <span @click="addNew">+</span>
+            </div>
+            <img
+              src="../../../../assets/icon/classroomObservation/voiceCard.png"
+            />
+          </div>
+        </div>
         <div class="sp_m_b_bottom">
-          <div  @click.stop="$emit('startTape')">
-						<div>开始录音</div>
-						<span>实时语音转录文字</span>
-						<span>智能课堂观察</span>
-						<img src="../../../../assets/icon/classroomObservation/startPageIcon2.png">
-					</div>
-          <div  @click.stop="$emit('uploadTape')">
-						<div>上传文件</div>
-						<span>录音复盘</span>
-						<span>一键分析课堂情况</span>
-						<img src="../../../../assets/icon/classroomObservation/startPageIcon1.png">
-					</div>
+          <div @click.stop="$emit('startTape')">
+            <div>开始录音</div>
+            <span>实时语音转录文字</span>
+            <span>智能课堂观察</span>
+            <img
+              src="../../../../assets/icon/classroomObservation/startPageIcon2.png"
+            />
+          </div>
+          <div @click.stop="$emit('uploadTape')">
+            <div>上传文件</div>
+            <span>录音复盘</span>
+            <span>一键分析课堂情况</span>
+            <img
+              src="../../../../assets/icon/classroomObservation/startPageIcon1.png"
+            />
+          </div>
         </div>
       </div>
       <!-- <div class="sp-m-item" @click.stop="$emit('startTape')">
@@ -78,12 +110,43 @@
 <script>
 export default {
   emits: ["startTape", "uploadTape"],
+  props: {
+    teacherVoiceprintList: {
+      type: Array,
+      default: () => []
+    },
+    chosenVoiceprint: {
+      type: Array,
+      default: []
+    },
+		controlsStatus:{
+			type:Number,
+			default:0
+		},
+  },
   data() {
     return {
-      showIntroduce: true
+      showIntroduce: true,
+      inputValue: [],
+      teacherList: []
     };
   },
-  methods: {}
+  watch: {
+    teacherVoiceprintList() {
+      this.teacherList = JSON.parse(JSON.stringify(this.teacherVoiceprintList));
+    },
+    chosenVoiceprint() {
+      this.inputValue = this.chosenVoiceprint;
+    }
+  },
+  methods: {
+    handleSelect(item) {
+      this.$parent.chosenVoiceprint = JSON.parse(JSON.stringify(this.inputValue));
+    },
+		addNew(){
+			this.$parent.onClickAddNewVoiceprint();
+		}
+  }
 };
 </script>
 
@@ -180,13 +243,20 @@ export default {
 .sp_m_box {
   width: 70%;
   height: 100%;
-	/* max-height: 550px; */
-	max-height: 400px;
+  max-height: 500px;
+  /* max-height: 400px; */
   display: flex;
   flex-direction: column;
   margin-bottom: 8%;
 }
 
+.sp_m_b_top {
+  width: 100%;
+  height: 220px;
+  /* background-color: red; */
+  margin-bottom: 20px;
+}
+
 .sp_m_b_bottom {
   width: 100%;
   flex: 1;
@@ -195,7 +265,6 @@ export default {
   justify-content: center;
 }
 
-
 .sp_m_b_bottom > div {
   height: 100%;
   flex: 1;
@@ -207,30 +276,30 @@ export default {
   box-shadow: 0px 4px 10px 0px #1d398314;
 
   box-shadow: 1px 1px 20px 4px #1d39830d;
-	background-color: #fff;
-	border-radius: 8px;
-	padding: 40px;
-	flex-direction: column;
-	cursor: pointer;
-	position: relative;
+  background-color: #fff;
+  border-radius: 8px;
+  padding: 40px;
+  flex-direction: column;
+  cursor: pointer;
+  position: relative;
 }
 
-.sp_m_b_bottom > div>img{
-	width: 50%;
-	position: absolute;
-	right: 0;
-	bottom: 0;
+.sp_m_b_bottom > div > img {
+  width: 50%;
+  position: absolute;
+  right: 0;
+  bottom: 0;
 }
 
-.sp_m_b_bottom > div>div{
-	font-size: 30px;
-	font-weight: bold;
-	margin-bottom: 10px;
+.sp_m_b_bottom > div > div {
+  font-size: 30px;
+  font-weight: bold;
+  margin-bottom: 10px;
 }
 
-.sp_m_b_bottom > div>span{
-	margin-top: 5px;
-	color: #00000066;
+.sp_m_b_bottom > div > span {
+  margin-top: 5px;
+  color: #00000066;
 }
 
 .sp_m_b_bottom > div:nth-of-type(2) {
@@ -301,7 +370,63 @@ export default {
   background-size: 100% 100%;
   margin-bottom: 15px;
 }
+.sp_m_b_t_voice {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  /* align-items: center; */
+  /* justify-content: center; */
+  box-sizing: border-box;
+  border: 1px solid #aeccfe;
+  box-shadow: 0px 4px 10px 0px #1d398314;
+
+  box-shadow: 1px 1px 20px 4px #1d39830d;
+  background-color: #fff;
+  border-radius: 8px;
+  padding: 40px;
+  flex-direction: column;
+  /* cursor: pointer; */
+  position: relative;
+}
+
+.sp_m_b_t_voice > img {
+  height: 80%;
+  position: absolute;
+  right: 0;
+  bottom: 0;
+}
+
+.sp_m_b_t_voice > .sp_m_b_t_v_title {
+  font-size: 30px;
+  font-weight: bold;
+  margin-bottom: 10px;
+}
+
+.sp_m_b_t_voice > span {
+  margin-top: 5px;
+  color: #00000066;
+}
 
+.sp_m_b_t_v_inputArea {
+  margin-top: 10px;
+  display: flex;
+  align-items: center;
+}
+
+.sp_m_b_t_v_inputArea > span {
+  width: 35px;
+  height: 35px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-color: #f3f7fd;
+  border-radius: 3px;
+  margin-left: 10px;
+  cursor: pointer;
+  font-weight: bold;
+  color: #3681fc;
+  font-size: 16px;
+}
 /* .sp-m-i-icon1 {
 	background: url("../../../../assets/icon/classroomObservation/tape.png")
 		no-repeat;

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

@@ -225,9 +225,26 @@ export default {
       tag: {
         0: "一",
         1: "二",
-        2: "三"
-      }
-    };
+        2: "三",
+				3: '四',
+				4: '五',
+				5: '六',
+				6: '七',
+				7:'八',
+				8:'九',
+				9:'十',
+				10:'十一',
+				11:'十二',
+				12:'十三',
+				13:'十四',
+				14:'十五',
+				15:'十六',
+				16:'十七',
+				17:'十八',
+				18:'十九',
+				19:'二十',
+    	},
+		}
   },
   methods: {
     //切换了课堂
@@ -505,56 +522,6 @@ export default {
 
         let analysisHtml = ``;
 
-        // tagList.forEach(i => {
-        //   let dire = `<div>`;
-        //   let tagHtml = `<div style="margin-bottom:100px">`;
-        //   if (i.value == 0) {
-        //     i.dataList = i.dataList.filter(i2 => i2.tIndex != 2);
-        //   }
-        //   i.dataList.sort((a, b) => a.tIndex - b.tIndex);
-
-        //   tagHtml += `<h1 style="font-size:16pt;margin-bottom:30px">${
-        //     this.tag[i.value]
-        //   }、${i.name}</h1>`;
-        //   dire += `<div style="font-size:14pt;margin:15px 0">${
-        //     this.tag[i.value]
-        //   }、${i.name}</div>`;
-        //   i.dataList.forEach(async (i2, i2Index) => {
-        //     tagHtml += `<h2 style="font-size:14pt;margin-bottom:20px">${i2Index +
-        //       1}、${
-        //       i2.jsonData.anotherName
-        //         ? i2.jsonData.anotherName
-        //         : i2.jsonData.name
-        //     }</h2>`;
-        //     dire += `<div style="font-size:11pt;margin:10px 0;margin-left:50px">${i2Index +
-        //       1}、${
-        //       i2.jsonData.anotherName
-        //         ? i2.jsonData.anotherName
-        //         : i2.jsonData.name
-        //     }</div>`;
-        //     if (showBrief) {
-        //       tagHtml += `<div style="font-size:10.5pt;font-style:italic;margin-bottom:10px;color:#6b798e">${i2.jsonData.result}</div>`;
-        //     }
-        //     if (i2.jsonData.eChartData) {
-        //       console.log(
-        //         await this.getEChartsImageSrc(i2.jsonData.eChartData)
-        //       );
-        //       // tagHtml += `<div>${await this.getEChartsImageSrc(
-        //       //   i2.jsonData.eChartData
-        //       // )}</div>`;
-        //       // tagHtml+=`<img src="${await this.getEChartsImageSrc(i2.jsonData.eChartData)}" style="width:400px;height:400px;"/>`
-        //     }
-        //     let _content = md.render(i2.jsonData.content);
-
-        //     tagHtml += `<div style="font-size:10.5pt;margin-bottom:10px">${_content}</div>`;
-        //   });
-
-        //   tagHtml += "</div>";
-        //   dire += "</div>";
-        //   analysisHtml += tagHtml;
-        //   directoryHtml += dire;
-        // });
-
         for (let c = 0; c < tagList.length; c++) {
           let i = tagList[c];
           let dire = `<div>`;

Diff do ficheiro suprimidas por serem muito extensas
+ 775 - 205
src/components/pages/components/exportDataDialog.vue


+ 1 - 1
src/components/pages/student/addCourse.vue

@@ -5494,7 +5494,7 @@ export default {
         }
       }
       let params = [{
-        uid: _user.join(","),
+        uid: array.join(","),
       }];
       this.ajax
         .post(this.$store.state.api + "getAllUserByIdP", params)

+ 1 - 1
src/components/pages/studentManage/student.vue

@@ -191,7 +191,7 @@
           <el-input disabled style="width: 300px" v-model="schoolName"></el-input>
         </el-form-item>
         <el-form-item label="班级" :label-width="formLabelWidth">
-          <el-select multiple collapse-tags v-model="sByClass" placeholder="请选择班级">
+          <el-select multiple collapse-tags v-model="sByClass" placeholder="请选择班级" filterable>
             <el-option v-for="(item, index) in classJuri" :key="index" :label="item.name" :value="item.id"></el-option>
           </el-select>
         </el-form-item>

+ 1 - 1
src/components/pages/studio/addCourse.vue

@@ -6537,7 +6537,7 @@ export default {
         }
       }
       let params = [{
-        uid: _user.join(","),
+        uid: array.join(","),
       }];
       this.ajax
         .post(this.$store.state.api + "getAllUserByIdP", params)

+ 50 - 44
src/components/pages/synergyCourse/addCourse.vue

@@ -1749,54 +1749,56 @@
       </span>
     </el-dialog> -->
     <el-dialog title="添加协同成员" :visible.sync="dialogVisibleMember" :append-to-body="true" width="25%" height="80%"
-      :before-close="handleClose" class="addNewPP customWidth">
-      <div class="people">
-        <div class="people_top">
-          <div class="people_top_right">
-            <div class="people_search">
-              <el-input placeholder="搜索完整的姓名/账号(不含邮箱后缀)" v-model="searchTN" @keyup.enter.native="getTeacher"></el-input>
-              <div class="search_img" @click="getTeacher">
-                <img src="../../../assets/icon/search.png" alt />
+      :before-close="handleClose" class="addNewPP customWidth" >
+      <div v-loading="teacherLoading">
+        <div class="people">
+          <div class="people_top">
+            <div class="people_top_right">
+              <div class="people_search">
+                <el-input placeholder="搜索完整的姓名/账号(不含邮箱后缀)" v-model="searchTN" @keyup.enter.native="getTeacher"></el-input>
+                <div class="search_img" @click="getTeacher">
+                  <img src="../../../assets/icon/search.png" alt />
+                </div>
               </div>
             </div>
+            <div class="people_nav">选择成员</div>
           </div>
-          <div class="people_nav">选择成员</div>
-        </div>
-        <div class="t_j_box" style="
-            padding: 20px 0 0 25px;
-            width: calc(100% - 55px);
-            margin-left: 25px;
-          ">
-          <span>姓名</span>
-          <span>身份</span>
-          <span>账号</span>
-          <span>学校</span>
+          <div class="t_j_box" style="
+              padding: 20px 0 0 25px;
+              width: calc(100% - 55px);
+              margin-left: 25px;
+            ">
+            <span>姓名</span>
+            <span>身份</span>
+            <span>账号</span>
+            <span>学校</span>
+          </div>
+          <div style="margin-left:25px;">
+            <el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange" style="display: flex;align-items: center;">全选</el-checkbox>
+          </div>
+          <el-checkbox-group v-model="checkboxList3" @change="handleCheckedTeacherJuriChange" class="people_name" v-if="teacherJuri.length">
+            <el-checkbox v-for="item in teacherJuri" :key="item.userid" :label="item.userid">
+              <div class="t_j_box">
+                <el-tooltip placement="top" :content="item.name ? item.name : '暂无姓名'">
+                  <span>{{ item.name ? item.name : "暂无姓名" }}</span>
+                </el-tooltip>
+                <span>{{ item.type == "1" ? "老师" : "学生" }}</span>
+                <el-tooltip placement="top" :content="item.username">
+                  <span>{{ item.username }}</span>
+                </el-tooltip>
+                <el-tooltip placement="top" :content="item.school">
+                  <span>{{ item.school }}</span>
+                </el-tooltip>
+              </div>
+            </el-checkbox>
+          </el-checkbox-group>
+          <div style="text-align: center; margin-top: 10px" v-else>暂无数据</div>
         </div>
-        <div style="margin-left:25px;">
-          <el-checkbox :indeterminate="isIndeterminate" v-model="checkAll" @change="handleCheckAllChange" style="display: flex;align-items: center;">全选</el-checkbox>
+        <div style="margin-top: 10px;" >
+          <el-pagination background layout="prev, pager, next" :page-size="pageSize" :total="total"
+            v-if="page && teacherJuri.length" style="padding-bottom: 20px"
+            @current-change="handleCurrentChange"></el-pagination>
         </div>
-        <el-checkbox-group v-model="checkboxList3" @change="handleCheckedTeacherJuriChange" class="people_name" v-if="teacherJuri.length">
-          <el-checkbox v-for="item in teacherJuri" :key="item.userid" :label="item.userid">
-            <div class="t_j_box">
-              <el-tooltip placement="top" :content="item.name ? item.name : '暂无姓名'">
-                <span>{{ item.name ? item.name : "暂无姓名" }}</span>
-              </el-tooltip>
-              <span>{{ item.type == "1" ? "老师" : "学生" }}</span>
-              <el-tooltip placement="top" :content="item.username">
-                <span>{{ item.username }}</span>
-              </el-tooltip>
-              <el-tooltip placement="top" :content="item.school">
-                <span>{{ item.school }}</span>
-              </el-tooltip>
-            </div>
-          </el-checkbox>
-        </el-checkbox-group>
-        <div style="text-align: center; margin-top: 10px" v-else>暂无数据</div>
-      </div>
-      <div style="margin-top: 10px;">
-        <el-pagination background layout="prev, pager, next" :page-size="pageSize" :total="total"
-          v-if="page && teacherJuri.length" style="padding-bottom: 20px"
-          @current-change="handleCurrentChange"></el-pagination>
       </div>
       <span slot="footer" class="dialog-footer">
         <el-button @click="page = 0;dialogVisibleMember = false;">取 消</el-button>
@@ -2946,10 +2948,11 @@ export default {
       imageList: [],
       heightPx: '100%',
       toolsData: JSON.parse(converter(JSON.stringify(toolsData))),
-      pageSize: 20,
+      pageSize: 200,
       total: 0,
       page: 0,
       isLoading2: false,
+      teacherLoading: false,
     };
   },
   directives: {
@@ -4682,6 +4685,7 @@ export default {
         });
     },
     getTeacher() {
+      this.teacherLoading = true;
       let params = {
         org:
           this.org && this.org != "undefined" && this.org != "null"
@@ -4712,8 +4716,10 @@ export default {
             }
           }
           this.teacherJuri = teacherJuri;
+          this.teacherLoading = false;
         })
         .catch((err) => {
+          this.teacherLoading = false;
           console.error(err);
         });
     },

+ 76 - 24
src/components/pages/test/check/aiBoxRight.vue

@@ -201,6 +201,7 @@
       <!-- @input="inputChange" -->
 
       <textarea
+        @input="inputChange"
         class="ai_body_input_textarea"
         @keydown="textareaKeydown"
         :disabled="isVoice"
@@ -512,6 +513,53 @@ export default {
     }
   },
   methods: {
+    // setJson(array){
+    //   const getAnswer = (j) => {
+    //     switch (j.type) {
+    //       case 1:
+    //         return j.json.array
+    //           .filter((_, idx) => j.json.answer2.includes(idx))
+    //           .map(item => `${item.img}${item.option}`)
+    //           .join(',');
+    //       case 3:
+    //       case 6:
+    //       case 7:
+    //       case 8:
+    //       case 11:
+    //         return j.json.answer2;
+    //       case 5:
+    //         if (!Array.isArray(j.json.file) || j.json.file.length === 0) {
+    //           return '无附件';
+    //         }
+    //         return j.json.file.map(file => `${file.name}(${file.url})`).join(',');
+    //       default:
+    //         return '';
+    //     }
+    //   };
+
+    //   this.answerArray = array.map(i => ({
+    //     "用户名": i.name,
+    //     "提交时间": i.time,
+    //     "表单内容": i.array.map((j, index) => ({
+    //       "序号": index + 1,
+    //       "题目": j.json.title,
+    //       "题目类型": this.options2[j.type],
+    //       "答案": getAnswer(j)
+    //     }))
+    //   }))
+    //   console.log(this.answerArray);
+
+    //   // 将JSON对象转换为字符串
+    //   const jsonString = JSON.stringify(this.answerArray, null, 2);
+
+    //   // 创建Blob对象
+    //   const blob = new Blob([jsonString], { type: "application/json" });
+    //   blob.lastModifiedDate = new Date();
+    //   blob.name = `表单数据.json`;
+
+    //   // 如果仍需要上传文件,可以保留这行
+    //   return this.uploadFile(blob);
+    // },
     setJson(array){
       const getAnswer = (j) => {
         switch (j.type) {
@@ -525,7 +573,7 @@ export default {
           case 7:
           case 8:
           case 11:
-            return j.json.answer2;
+            return j.json.answer2.replace(/\n/g, ' '); // 去除回车
           case 5:
             if (!Array.isArray(j.json.file) || j.json.file.length === 0) {
               return '无附件';
@@ -536,25 +584,29 @@ export default {
         }
       };
 
-      this.answerArray = array.map(i => ({
-        "用户名": i.name,
-        "提交时间": i.time,
-        "表单内容": i.array.map((j, index) => ({
-          "序号": index + 1,
-          "题目": j.json.title,
-          "题目类型": this.options2[j.type],
-          "答案": getAnswer(j)
-        }))
-      }))
-      console.log(this.answerArray);
-
-      // 将JSON对象转换为字符串
-      const jsonString = JSON.stringify(this.answerArray, null, 2);
+      // 获取所有题目类型和题目
+      const questions = array[0].array.map((j, index) => ({
+        "序号": index + 1,
+        "题目类型": this.options2[j.type],
+        "题目": j.json.title
+      }));
+
+      // 构建CSV内容
+      let csvContent = "用户名 | 提交时间 | " + questions.map(q => `${q.序号}-${q.题目类型}-${q.题目}`).join(' | ') + "\n";
+
+      // 添加每个用户的答案
+      array.forEach(i => {
+        let row = [i.name, i.time];
+        i.array.forEach(j => {
+          row.push(getAnswer(j));
+        });
+        csvContent += row.join(' | ') + "\n";
+      });
 
       // 创建Blob对象
-      const blob = new Blob([jsonString], { type: "application/json" });
+      const blob = new Blob([csvContent], { type: "text/plain;charset=utf-8" });
       blob.lastModifiedDate = new Date();
-      blob.name = `表单数据.json`;
+      blob.name = `表单数据.txt`;
 
       // 如果仍需要上传文件,可以保留这行
       return this.uploadFile(blob);
@@ -680,7 +732,7 @@ export default {
         this.faloading = true;
         if (_atRoleList.length > 0) {
           //有@角色
-          let _replaceText = `${message}`;
+          let _replaceText = `Role: 你是数据检索大师,可以利用file_search的方式完整的去分析文件内容 Language: Please use the same language as the user requirement, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\n${message}`;
           let _htmlText = message;
           _atRoleList.forEach(_i => {
             _replaceText = _replaceText.replaceAll(`@${_i.assistantName}`, ``);
@@ -735,7 +787,7 @@ export default {
               message: _replaceText,
               session_name: `${this.courseId}-${this.userid}-test`,
               uid: _uid,
-              file_ids: [this.fileId],
+              file_ids: this.fileId ? [this.fileId] : [],
               // model: "gpt-4o-2024-08-06"
               model: "qwen-plus"
             };
@@ -831,10 +883,10 @@ export default {
           let params = {
             assistant_id: 'cd72354e-7be5-11ef-a263-12e77c4cb76b',
             userId: this.userid,
-            message: message,
+            message: `Role: 你是数据检索大师,可以利用file_search的方式完整的去分析文件内容 Language: Please use the same language as the user requirement, if the user speaks Chinese, the specific text of your answer should also be in Chinese.\n${message}`,
             session_name: `${this.courseId}-${this.userid}-test`,
             uid: _uuid,
-            file_ids: [this.fileId],
+            file_ids: this.fileId ? [this.fileId] : [],
             model: "gpt-4o-2024-08-06"
           };
           this.$nextTick(() => {
@@ -1091,7 +1143,7 @@ export default {
     },
     inputChange() {
       if (this.courseText.at(-1) == "@") {
-        this.showRoleList = true;
+        // this.showRoleList = true;
       }
       if (this.courseText.at(-1) == "/") {
         console.log("哇卡ka2");
@@ -1627,11 +1679,11 @@ export default {
   margin: 10px 5px 10px 5px;
   min-height: 35px;
   height: 35px;
-  max-height: 60vh;
+  max-height: 100px;
   border: none;
   outline: none;
   resize: none;
-  font-size: 18px;
+  font-size: 16px;
   overflow: auto;
   padding-right: 100px;
   background-color: #fff !important;

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

@@ -0,0 +1,557 @@
+<template>
+  <el-dialog
+    :visible.sync="show"
+    :append-to-body="true"
+    title="word导出"
+    width="1000px"
+    top="10vh"
+    :before-close="handleClose"
+    class="dialog_diy"
+  >
+    <div class="box" v-loading="loading">
+      <div class="b_left"></div>
+      <div class="b_right">
+        <div class="d_box">
+          <div class="d_b_step">
+            <h2>第一步:下载模板文档</h2>
+            <p>点击下载模板文档来下载指定文档</p>
+            <el-button
+              class="d_b_s_button"
+              type="primary"
+              :disabled="!downFileData"
+              @click="downloadTemplateDocx()"
+              >下载模板文档</el-button
+            >
+          </div>
+
+          <div class="d_b_step">
+            <h2>第二步:填入模板变量</h2>
+            <p>按需制作排版Word模板文档,目前支持docx格式</p>
+            <p>
+              在文件中指定位置输入下方的"字段变量",用于显示真实数据的准确位置
+            </p>
+            <img
+              src="https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2F%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_202410090922471728436994846.png"
+            />
+            <div
+              v-for="(item, index) in fieldList"
+              :key="index"
+              class="d_b_s_fieldListItem"
+            >
+              <span
+                >{{ item.name }}:{{
+                  "{" + (item.type == "image" ? "%" : "") + item.field + "}"
+                }}</span
+              >
+              <span @click="copyContent(`{${item.type == 'image' ? '%' : ''}${item.field}}`)">
+                <svg
+                  width="14"
+                  height="14"
+                  viewBox="0 0 14 14"
+                  xmlns="http://www.w3.org/2000/svg"
+                >
+                  <path
+                    fill-rule="evenodd"
+                    clip-rule="evenodd"
+                    d="M1.85645 2.28599C1.85645 2.0493 2.04832 1.85742 2.28502 1.85742H8.71359C8.95028 1.85742 9.14216 2.0493 9.14216 2.28599C9.14216 2.52269 8.95028 2.71456 8.71359 2.71456H2.71359V8.71456C2.71359 8.95126 2.52171 9.14314 2.28502 9.14314C2.04832 9.14314 1.85645 8.95126 1.85645 8.71456V2.28599Z"
+                  />
+                  <path
+                    fill-rule="evenodd"
+                    clip-rule="evenodd"
+                    d="M4.42871 4.85631C4.42871 4.61961 4.62059 4.42773 4.85728 4.42773H11.7144C11.9511 4.42773 12.143 4.61961 12.143 4.85631V11.7134C12.143 11.9501 11.9511 12.142 11.7144 12.142H4.85728C4.62059 12.142 4.42871 11.9501 4.42871 11.7134V4.85631ZM5.28585 5.28488V11.2849H11.2859V5.28488H5.28585Z"
+                  />
+                </svg>
+              </span>
+            </div>
+          </div>
+
+          <div class="d_b_step">
+            <h2>第三步:上传填入后的模板文档</h2>
+            <div v-if="uploadTemplateDocxData" class="d_b_s_fileCard">
+              <svg
+                t="1728376433028"
+                class="icon"
+                viewBox="0 0 1024 1024"
+                version="1.1"
+                xmlns="http://www.w3.org/2000/svg"
+                p-id="5172"
+                width="200"
+                height="200"
+              >
+                <path
+                  d="M815.5 160v704c0 17.7-14.3 32-32 32h-543c-17.7 0-32-14.3-32-32V160c0-17.7 14.3-32 32-32h543c17.7 0 32 14.3 32 32z m0-96h-607c-35.3 0-64 28.7-64 64v768c0 35.3 28.7 64 64 64h607c35.3 0 64-28.7 64-64V128c0-35.3-28.7-64-64-64z"
+                  p-id="5173"
+                ></path>
+                <path
+                  d="M703.5 320h-384c-17.7 0-32-14.3-32-32s14.3-32 32-32h384c17.7 0 32 14.3 32 32s-14.3 32-32 32zM703.5 512h-384c-17.7 0-32-14.3-32-32s14.3-32 32-32h384c17.7 0 32 14.3 32 32s-14.3 32-32 32zM511.5 704h-192c-17.7 0-32-14.3-32-32s14.3-32 32-32h192c17.7 0 32 14.3 32 32s-14.3 32-32 32z"
+                  p-id="5174"
+                ></path>
+              </svg>
+
+              <span>{{ uploadTemplateDocxData.name }}</span>
+              <span class="d_b_s_f_c_del" @click="clearUploadTemplateDocxData()"
+                >删除</span
+              >
+            </div>
+            <el-button
+              class="d_b_s_button"
+              type="primary"
+              :disabled="uploadTemplateDocxData != null"
+              @click="uploadTemplateDocx()"
+              >上传填入后的模板文档</el-button
+            >
+          </div>
+
+          <div class="d_b_step">
+            <h2>第四步:点击导出</h2>
+            <p></p>
+            <el-button
+              class="d_b_s_button"
+              type="primary"
+              :disabled="!uploadTemplateDocxData"
+              @click="exportDocx()"
+              >导出Word文档</el-button
+            >
+          </div>
+        </div>
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import PizZip from "pizzip";
+import Docxtemplater from "docxtemplater";
+import ImageModule from "docxtemplater-image-module-free";
+import { saveAs } from 'file-saver';
+
+const getFile = url => {
+  return new Promise((resolve, reject) => {
+    var credentials = {
+      accessKeyId: "AKIATLPEDU37QV5CHLMH",
+      secretAccessKey: "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"
+    }; //秘钥形式的登录上传
+    window.AWS.config.update(credentials);
+    window.AWS.config.region = "cn-northwest-1"; //设置区域
+    let url2 = url;
+    let _url2 = "";
+    if (
+      url2.indexOf("https://view.officeapps.live.com/op/view.aspx?src=") != -1
+    ) {
+      _url2 = url2.split(
+        "https://view.officeapps.live.com/op/view.aspx?src="
+      )[1];
+    } else {
+      _url2 = url2;
+    }
+    var s3 = new window.AWS.S3({ params: { Bucket: "ccrb" } });
+    let name = decodeURIComponent(
+      _url2.split("https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/")[1]
+    );
+    var params = {
+      Bucket: "ccrb",
+      Key: name
+    };
+    s3.getObject(params, function(err, data) {
+      if (err) {
+        console.log(err, err.stack);
+        resolve({ data: 1 });
+      } else {
+        resolve({ data: data.Body });
+        console.log(data);
+      }
+    });
+  });
+};
+
+export default {
+  props: {},
+  data() {
+    return {
+      show: false,
+      loading: false,
+      fieldList: [
+        { name: "第一题", field: "ti_01", type: "text", value: "第一题ti_01" },
+        { name: "第二题", field: "ti_02", type: "text", value: "第二题ti_02" },
+        { name: "第三题", field: "ti_03", type: "text", value: "第三题ti_03" },
+        { name: "第四题", field: "ti_04", type: "text", value: "第四题ti_04" },
+        { name: "第五题", field: "ti_05", type: "text", value: "第五题ti_05" },
+        {
+          name: "图片1",
+          field: "image_01",
+          type: "image",
+          value:
+            "https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2F%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_202410090922471728436994846.png"
+        }
+      ],
+      uploadTemplateDocxData: null, //上传的模板文档
+      downFileData: {
+        fileName: "大学生重补免缓考申请表.docx",
+        url:
+          "https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/default%2F%E5%A4%A7%E5%AD%A6%E7%94%9F%E9%87%8D%E8%A1%A5%E5%85%8D%E7%BC%93%E8%80%83%E7%94%B3%E8%AF%B7%E8%A1%A81728378389891.docx"
+      } // 下载模板文档的url
+    };
+  },
+  methods: {
+    handleClose(done) {
+      this.close();
+      done();
+    },
+    open() {
+      this.show = true;
+    },
+    close() {
+      this.show = false;
+    },
+    init() {
+      // 初始化
+    },
+    downloadTemplateDocx() {
+      getFile(this.downFileData.url).then(data => {
+        if (data.data != 1) {
+          // 下载文件, 并存成ArrayBuffer对象
+          const file_name = this.downFileData.fileName; // 获取文件名
+          const file_data = data.data; // 获取文件数据
+          let url = window.URL.createObjectURL(new Blob([file_data]));
+          let a = document.createElement("a");
+          a.name = file_name;
+          a.href = url;
+          a.download = file_name;
+          a.click();
+          console.log(data);
+          this.$message.success("下载成功");
+        } else {
+          this.$message.error("下载失败");
+        }
+      });
+    },
+    uploadTemplateDocx() {
+      let input = document.createElement("input");
+      input.type = "file";
+      // input.accept = ".wav";
+      // input.accept = "audio/*, .txt, .pdf, .xlsx";
+      input.accept = ".docx";
+      input.click();
+      input.onchange = () => {
+        this.loading = true;
+        let file = input.files[0];
+        if (!/\.(docx)$/i.test(file.name)) {
+          this.loading = false;
+          return this.$message.error("请上传.docx格式的文件");
+        }
+        console.log(file);
+        this.uploadTemplateDocxData = file;
+        this.loading = false;
+        // this.uploadWavFileAndGetText(file);
+      };
+    },
+    async exportDocx() {
+      if (!this.uploadTemplateDocxData)
+        return this.$message.error("请先上传模板文档");
+      let reader = new FileReader();
+      reader.readAsArrayBuffer(this.uploadTemplateDocxData);
+      reader.onload = async e => {
+        try {
+          this.loading = true;
+          const binary = new Uint8Array(reader.result);
+          //创建一个PizZip实例
+          const zip = new PizZip(binary);
+          // 将模板内容加载到 Docxtemplater 中
+          const doc = new Docxtemplater().loadZip(zip);
+
+          let _data = {};
+          let _image = {};
+          // 设置模板值
+          // this.fieldList.forEach(i => {
+          //   _data[i.field] = i.value;
+          // });
+          for (let i = 0; i < this.fieldList.length; i++) {
+						// console.log(this.fieldList[i])
+            if (this.fieldList[i].type == "text") {
+              //文本处理
+              _data[this.fieldList[i].field] = this.fieldList[i].value;
+            } else if (this.fieldList[i].type == "image") {
+              //图片处理
+              let _imageObj = await this.convertImageUrlToBase64(
+                this.fieldList[i].value
+              );
+              _data[this.fieldList[i].field] = _imageObj.url;
+              _image[this.fieldList[i].field] = {
+                width: _imageObj.width,
+                height: _imageObj.height
+              };
+            }
+          }
+					// return this.loading = false;
+
+          // 图片处理
+          const opts = {
+            centered: false,
+            fileType: "docx",
+            getImage: (value, value2, value3) => {
+              return this.base64DataURLToArrayBuffer(value);
+            },
+            getSize: (arrayValue, value, tagName) => {
+							// console.log(_image)
+							// console.log(tagName)
+              let newWidth = _image[tagName].width;
+              let newHeight = _image[tagName].height;
+
+							// let newWidth = 550;
+              // let newHeight = 100;
+              return [newWidth, newHeight];
+            }
+          };
+					doc.attachModule(new ImageModule(opts));
+          //渲染模板
+          doc.setData(_data);
+          doc.render();
+          //获取渲染后的文本
+          const output = doc.getZip().generate({
+            type: "blob",
+            mimeType:
+              "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
+            compression: "DEFLATE"
+          });
+
+					saveAs(output,`${this.uploadTemplateDocxData.name}`)
+          // let link = document.createElement("a");
+          // link.download = this.uploadTemplateDocxData.name;
+          // link.style.display = "none";
+          // let blob = new Blob([output]);
+          // link.href = URL.createObjectURL(blob);
+          // document.body.appendChild(link);
+          // link.click();
+          // document.body.removeChild(link);
+          this.loading = false;
+          this.$message.success("导出成功");
+        } catch (error) {
+          console.log(error);
+          this.loading = false;
+          return this.$message.error("导出失败");
+        }
+      };
+    },
+    clearUploadTemplateDocxData() {
+      this.uploadTemplateDocxData = null;
+    },
+    copyContent(content) {
+      const input = document.createElement("input");
+      // 设置 display为none会导致无法复制
+      // input.style.display = "none";
+      // 所以只能用其他方法隐藏
+      input.style.opacity = 0;
+      // 为了不影响布局
+      input.style.position = "fixed";
+      input.style.left = "-100%";
+      input.style.top = "-100%";
+      input.value = content;
+      document.body.appendChild(input);
+      input.select();
+      const success = document.execCommand("copy");
+      document.body.removeChild(input);
+      if (!success) {
+        return this.$message.error("复制失败");
+      } else {
+        return this.$message.success("复制成功");
+      }
+    },
+    convertImageUrlToBase64(imageUrl) {
+      return new Promise((resolve, reject) => {
+        const img = new Image();
+        img.crossOrigin = "Anonymous"; // 允许跨域请求
+        img.src = imageUrl;
+
+        img.onload = () => {
+          const canvas = document.createElement("canvas");
+          canvas.width = img.width;
+          canvas.height = img.height;
+          const ctx = canvas.getContext("2d");
+          ctx.drawImage(img, 0, 0);
+          const base64 = canvas.toDataURL("image/png");
+          resolve({ url: base64, width: img.width, height: img.height });
+        };
+
+        img.onerror = error => {
+					console.log("图片转base64失败")
+					console.log(error)
+          resolve({url:"",width:0,height:0});
+        };
+      });
+    },
+    base64DataURLToArrayBuffer(dataURL) {
+      const base64Regex = /^data:image\/(png|jpg|svg|svg\+xml);base64,/;
+      if (!base64Regex.test(dataURL)) {
+        return false;
+      }
+      const stringBase64 = dataURL.replace(base64Regex, "");
+      let binaryString;
+      if (typeof window !== "undefined") {
+        binaryString = window.atob(stringBase64);
+      } else {
+        binaryString = new Buffer(stringBase64, "base64").toString("binary");
+      }
+      const len = binaryString.length;
+      const bytes = new Uint8Array(len);
+      for (let i = 0; i < len; i++) {
+        const ascii = binaryString.charCodeAt(i);
+        bytes[i] = ascii;
+      }
+      return bytes.buffer;
+    }
+  }
+};
+</script>
+
+<style scoped>
+.dialog_diy >>> .el-dialog {
+  /* height: 100%; */
+  /* margin: 0 auto !important; */
+}
+
+.dialog_diy >>> .el-dialog__header {
+  background: #fff !important;
+  padding: 15px 20px;
+}
+
+.dialog_diy >>> .el-dialog__body {
+  height: calc(100% - 124px);
+  box-sizing: border-box;
+  padding: 0px;
+}
+
+.dialog_diy >>> .el-dialog__title {
+  color: #000;
+}
+
+.dialog_diy >>> .el-dialog__headerbtn {
+  top: 19px;
+}
+
+.dialog_diy >>> .el-dialog__headerbtn .el-dialog__close {
+  color: #000;
+}
+
+.dialog_diy >>> .el-dialog__headerbtn .el-dialog__close:hover {
+  color: #000;
+}
+
+.dialog_diy >>> .el-dialog__body,
+.dialog_diy >>> .el-dialog__footer {
+  background: #fff;
+}
+
+.box {
+  width: 100%;
+  height: 70vh;
+  padding: 0 20px 15px;
+  display: flex;
+  box-sizing: border-box;
+}
+
+.b_left {
+  width: 400px;
+  height: 100%;
+}
+
+.b_right {
+  flex: 1;
+  height: 100%;
+}
+
+.d_box {
+  width: 100%;
+  height: 100%;
+  overflow: auto;
+}
+
+.d_b_step {
+  width: 100%;
+  max-width: 100%;
+  height: auto;
+  padding: 20px;
+  box-sizing: border-box;
+  /* background-color: red; */
+}
+
+.d_b_step > h2 {
+  margin-bottom: 10px;
+}
+
+.d_b_step > p {
+  font-size: 16px;
+}
+
+.d_b_step > img {
+  width: 100%;
+  margin-top: 10px;
+}
+
+.d_b_s_button {
+  margin-top: 10px;
+}
+
+.d_b_s_fieldListItem {
+  margin-top: 15px;
+  font-size: 16px;
+  display: flex;
+  align-items: center;
+}
+
+.d_b_s_fieldListItem > span {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.d_b_s_fieldListItem > span > svg {
+  width: 15px;
+  height: 15px;
+  cursor: pointer;
+  fill: #000;
+  margin-left: 10px;
+  transition: 0.3s;
+  fill-opacity: 0.6;
+}
+
+.d_b_s_fieldListItem > span > svg:hover {
+  fill: #409eff;
+  fill-opacity: 1;
+}
+
+.d_b_s_fileCard {
+  width: 100%;
+  height: 45px;
+  border-radius: 2px;
+  margin-bottom: 10px;
+  display: flex;
+  align-items: center;
+  box-sizing: border-box;
+  padding: 0 10px;
+  border: solid 1px #dfdfdf;
+  position: relative;
+  cursor: default;
+}
+
+.d_b_s_fileCard > svg {
+  width: 30px;
+  height: 30px;
+  fill: #a3a8ac;
+  margin-right: 10px;
+}
+
+.d_b_s_fileCard > span {
+  max-width: calc(100% - 100px);
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  font-size: 16px;
+}
+
+.d_b_s_f_c_del {
+  position: absolute;
+  right: 10px;
+  cursor: pointer;
+  color: #e60012;
+}
+</style>

+ 15 - 3
src/components/pages/test/check/index.vue

@@ -47,7 +47,8 @@
                 <div class="right" v-if="isDesktop">
                   <div class="data_box">
                     <span>提交数量</span>
-                    <span><span class="big">{{ works.length }}</span>份</span>
+                    <!-- <span><span class="big">{{ works.length }}</span>份</span> -->
+                    <span><span class="big">{{ worksArray.length }}</span>份</span>
                   </div>
                   <div class="data_box" v-if="testJson.juriP">
                     <span>表单完成率</span>
@@ -123,7 +124,7 @@
                   </div>
                 </div>
                 <div class="btnA" v-if="stype == 3" @click="exportAllWord2">导出人员数据</div>
-                <div class="btnA" @click="openChat" v-show="false" >AI分析</div><!--v-if="this.worksArray.length"-->
+                <div class="btnA" @click="openChat" v-if="this.worksArray.length">AI分析</div><!--v-if="this.worksArray.length"-->
               </div>
             </div>
              <!-- 手机端 查看切换 isDesktop-->
@@ -632,6 +633,7 @@
                 <template slot-scope="scope">
                   <el-button @click="getTest(scope.row)" type="primary" size="small">查看</el-button>
                   <el-button @click="setWordHtml(scope.row)" type="primary" size="small">导出答题信息</el-button>
+									<!-- <el-button @click="setWordTemplate(scope.row)" type="primary" size="small">word导出</el-button> -->
                   <el-button @click="deleteTest(scope.row.id)" type="primary" size="small">删除</el-button>
                 </template>
               </el-table-column>
@@ -892,6 +894,8 @@
     <wpdf :dialogVisiblePdf.sync="dialogVisiblePdf" :url="wurl"></wpdf>
     <wVideo :dialogVisibleVideo.sync="dialogVisibleVideo" :url="wurl"></wVideo>
     <wOffice :dialogVisibleOffice.sync="dialogVisibleOffice" :url="wurl"></wOffice>
+
+		<docxTemplateDialog ref="docxTemplateDialogRef"/>
   </div>
 </template>
 
@@ -915,6 +919,9 @@ import FileSaver from "file-saver";
 import XLSX from "xlsx-js-style";
 import aiBoxRight from './aiBoxRight.vue'
 
+// word模板
+import docxTemplateDialog from './docxTemplateDialog' 
+
 const getFile = (url) => {
     return new Promise((resolve, reject) => {
         var credentials = {
@@ -962,7 +969,8 @@ export default {
     wOffice,
     checkPie,
     wordcloud,
-    aiBoxRight
+    aiBoxRight,
+		docxTemplateDialog
   },
   data() {
     return {
@@ -2325,6 +2333,10 @@ export default {
           console.error(err);
         });
     },
+		setWordTemplate(item){
+			this.$refs.docxTemplateDialogRef.open()//这里可以传数据
+			// console.log(item)
+		}
   },
   beforeDestroy() {
     document.getElementsByTagName('html')[0].style.overflow = '';

+ 9 - 0
src/components/pages/test/dataCom/cascader.vue

@@ -143,14 +143,23 @@ export default {
             this.show = false
         },
         selectOption(option) {
+            if(this.checkF == option.id){
+                return
+            }
             console.log(option);
             this.checkF2 = '';
             this.teacher = []
             this.checkF = option.id;
             this.children = option.child;
+            if(option.child.length){
+                this.selectOption2(option.child[0])
+            }
             this.$forceUpdate();
         },
         selectOption2(option) {
+            if(this.checkF2 == option.id){
+                return
+            }
             this.checkF2 = option.id;
             this.teacher = []
             this.loading = true

+ 28 - 8
src/components/pages/test/dataCom/radarZong.vue

@@ -117,6 +117,8 @@ export default {
                         value: option.xdata2,
                         name: '平均得分率'
                     }
+                }else {
+                    this.option.series[0].data[1] = []
                 }
                 // this.option.series[0].data[0].value = option.xdata;
                 // 初始化雷达图
@@ -125,21 +127,36 @@ export default {
             });
         },
         setArray(array, type) {
-            this.ooption.sdata = Object.keys(array).map((item) => {
-                return {
-                    name: item,
-                    max: 100,
-                };
-            });
+            if(Object.keys(array).length){
+                this.ooption.sdata =  Object.keys(array).map((item) => {
+                    return {
+                        name: item,
+                        max: 100,
+                    };
+                });
+            }else if(this.evCourseArray.length){
+                this.ooption.sdata =  Object.keys(this.evCourseArray).map((item) => {
+                    return {
+                        name: item,
+                        max: 100,
+                    };
+                });
+                this.ooption.xdata = Object.keys(this.evCourseArray).map((item) => {
+                    return array[item].cogScore;
+                })
+                this.ooption.xdata2 = []
+            }
             if (type == 1) {
                 this.ooption.xdata = Object.keys(array).map((item) => {
                     return array[item].cogScore;
                 })
+                this.ooption.xdata2 = []
             }
             if (type == 2) {
-                this.ooption.xdata2 = Object.keys(array).map((item) => {
+
+                this.ooption.xdata2 = Object.keys(array).length ? Object.keys(array).map((item) => {
                     return array[item].cogScore;
-                })
+                }) : []
             }
             if (!this.chartObj) {
                 this.setChart(this.ooption);
@@ -176,6 +193,8 @@ export default {
                         value: this.ooption.xdata2,
                         name: '平均得分率'
                     }
+                }else {
+                    this.option.series[0].data[1] = []
                 }
                 this.chartObj.setOption(this.option);
             }
@@ -203,6 +222,7 @@ export default {
             handler(newValue, oldValue) {
                 this.ooption.xdata2 = [];
                 this.setArray(newValue, 2)
+                console.log(2222222222222222);
                 this.$forceUpdate();
             },
         },

+ 15 - 6
src/components/pages/test/databoard.vue

@@ -53,15 +53,15 @@
 
             </div>
             <div class="randarZong">
-                <radarZong :evCourseArray="zongJson" :evCourseArray2="zongJson2" v-if="teacherArray.length"></radarZong>
-                <radarZong :evCourseArray="zongJson" v-else></radarZong>
+                <radarZong :evCourseArray="zongJson" :evCourseArray2="zongJson2" v-if="checkSet"></radarZong>
+                <radarZong :evCourseArray="zongJson2" :evCourseArray2="{}" v-else></radarZong>
             </div>
             <div class="randarBox">
                 <div class="title">教师详情</div>
                 <div class="randarTeacher" v-for="(item, index) in tableData" :key="index">
                     <div class="randarTitle">
                         <span>{{ item.username }}</span>
-                        <span>{{ getScore(item.json) }}</span>
+                        <span>{{ item.score }}</span>
                     </div>
                     <div class="randar">
                         <radarTeacher :evCourseArray="zongJson2" :evCourseArray2="item.json"></radarTeacher>
@@ -221,7 +221,12 @@ export default {
                         zongJson[zongArray[i]].cogScore = (zongJson[zongArray[i]].cogScore / data.length).toFixed(0)
                     }
 
-                    this.tableData = _data;
+                    this.tableData = _data
+                        .filter((e) => {
+                            e.score = this.getScore(e.json);
+                            return e;
+                        })
+                        .sort((a, b) => b.score - a.score);
                     console.log(this.tableData);
                     this.zongJson = zongJson
                     this.zongJson2 = zongJson
@@ -247,7 +252,7 @@ export default {
             let checkSet = this.checkSet
             let _data = []
             let zongJson = {}
-            let data = this.data
+            let data = JSON.parse(JSON.stringify(this.data))
             if(this.teacherArray.length){
                 data = data.filter((e) => {
                     return this.teacherArray.includes(e.userid)
@@ -301,7 +306,11 @@ export default {
                 zongJson[zongArray[i]].evaScore = (zongJson[zongArray[i]].evaScore / data.length).toFixed(0)
                 zongJson[zongArray[i]].cogScore = (zongJson[zongArray[i]].cogScore / data.length).toFixed(0)
             }
-            this.tableData = _data;
+            this.tableData = _data.filter((e) => {
+                e.score = this.getScore(e.json);
+                return e;
+            })
+            .sort((a, b) => b.score - a.score);
             console.log(this.tableData);
             this.zongJson2 = zongJson
             console.log(zongJson);

+ 3 - 2
src/components/pages/works.vue

@@ -1105,10 +1105,11 @@ export default {
 }
 
 .worksDialogCSSExp >>> .el-dialog__body {
-  width: 635px !important;
+  width: 100% !important;
+  padding: 0 !important;
 }
 .worksDialogCSSExp >>> .el-dialog {
-  width: 676px !important;
+  width: 594pt !important;
 }
 /* .TableCss >>> .el-table-column--selection .cell{
   display: none;

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff