Explorar el Código

修改多模态接口

yangg hace 8 meses
padre
commit
eeb793b366
Se han modificado 65 ficheros con 1359 adiciones y 361 borrados
  1. 1 1
      .env.development
  2. 1 1
      .env.production
  3. 0 0
      dist/index.html
  4. 0 0
      dist/static/css/app.d60322ec.css
  5. 0 0
      dist/static/css/chunk-03ae2181.0c3c86c9.css
  6. 1 0
      dist/static/css/chunk-1bde5712.d676c2aa.css
  7. 0 0
      dist/static/css/chunk-3777bfcd.f5399100.css
  8. 0 0
      dist/static/css/chunk-38db9d02.3abc2536.css
  9. 1 0
      dist/static/css/chunk-3ecbedba.45f99df9.css
  10. 0 1
      dist/static/css/chunk-4a670a7f.e09c1a2a.css
  11. 0 1
      dist/static/css/chunk-6c81d65e.7487f033.css
  12. 0 0
      dist/static/js/app.5b8d40a1.js
  13. 0 0
      dist/static/js/app.981f7a47.js
  14. 0 0
      dist/static/js/chunk-00d3f6c2.09e66246.js
  15. 0 0
      dist/static/js/chunk-00d3f6c2.aa3f606c.js
  16. 0 0
      dist/static/js/chunk-03ae2181.d253277f.js
  17. 0 0
      dist/static/js/chunk-0678749f.bb74f10c.js
  18. 0 0
      dist/static/js/chunk-09f78c22.10e2d4c9.js
  19. 0 0
      dist/static/js/chunk-0ec56e2a.ab0452b7.js
  20. 0 0
      dist/static/js/chunk-10b289e2.6be4e4a1.js
  21. 0 0
      dist/static/js/chunk-11ff1a32.61a74561.js
  22. 0 0
      dist/static/js/chunk-138f7583.43c23be9.js
  23. 0 0
      dist/static/js/chunk-16a60d86.90d8f25a.js
  24. 0 0
      dist/static/js/chunk-194e2959.d01d3364.js
  25. 0 0
      dist/static/js/chunk-1bde5712.06261586.js
  26. 0 0
      dist/static/js/chunk-23d4d74d.2794e8b8.js
  27. 0 0
      dist/static/js/chunk-2d0d3705.8eb62e74.js
  28. 0 0
      dist/static/js/chunk-35464144.15682d7b.js
  29. 0 0
      dist/static/js/chunk-3777bfcd.a0fbf115.js
  30. 0 0
      dist/static/js/chunk-38db9d02.1d4aee9b.js
  31. 0 0
      dist/static/js/chunk-3ecbedba.fb9abb66.js
  32. 0 0
      dist/static/js/chunk-44eee83d.9d235966.js
  33. 0 0
      dist/static/js/chunk-4a670a7f.e5cc1146.js
  34. 0 0
      dist/static/js/chunk-4ac7f82e.ef61581d.js
  35. 0 0
      dist/static/js/chunk-52f685c2.bb5a2f67.js
  36. 0 0
      dist/static/js/chunk-5a3bdf57.ef1ef08f.js
  37. 0 0
      dist/static/js/chunk-616f7602.63185d7a.js
  38. 0 0
      dist/static/js/chunk-6711f3a4.253a954d.js
  39. 0 0
      dist/static/js/chunk-6711f3a4.28993305.js
  40. 0 0
      dist/static/js/chunk-674d021c.3502f874.js
  41. 0 0
      dist/static/js/chunk-683feb49.ab95f8ec.js
  42. 0 0
      dist/static/js/chunk-6a80e4ca.c571b091.js
  43. 0 0
      dist/static/js/chunk-6c81d65e.10bd263e.js
  44. 0 0
      dist/static/js/chunk-6ddf6871.ea368fb3.js
  45. 0 0
      dist/static/js/chunk-708604c0.6338ce53.js
  46. 0 0
      dist/static/js/chunk-7150dcbc.7adf2888.js
  47. 0 0
      dist/static/js/chunk-77e7ad00.4552a5d5.js
  48. 0 0
      dist/static/js/chunk-77e7ad00.6c1dc206.js
  49. 0 0
      dist/static/js/chunk-7a88741c.70c1d7e3.js
  50. 0 0
      dist/static/js/chunk-9235c1ae.7740a205.js
  51. 0 0
      dist/static/js/chunk-libs.188d12a6.js
  52. 10 0
      src/api/knowledge.js
  53. 4 2
      src/components/CanvasEditor/index.vue
  54. 5 5
      src/components/FilePreview/index.vue
  55. 279 13
      src/components/ai.vue
  56. 3 3
      src/components/chartIcon/index.vue
  57. 15 9
      src/views/Information/index.vue
  58. 19 0
      src/views/knowledgeMenu/category/components/dataSearch.vue
  59. 5 3
      src/views/knowledgeMenu/category/infoList.vue
  60. 6 2
      src/views/knowledgeMenu/category/knowledgeSet.vue
  61. 101 58
      src/views/login/aiIndex.vue
  62. 24 10
      src/views/middPage/index.vue
  63. 263 132
      src/views/middPage/list.vue
  64. 3 0
      src/views/project/ProjectInput.vue
  65. 618 120
      src/views/project/components/dataList.vue

+ 1 - 1
.env.development

@@ -2,7 +2,7 @@
 ENV = 'development'
 port = 8080
 # base api
-VUE_APP_BASE_AI_API='https://ylaiapi.raycos.com.cn'
+VUE_APP_BASE_AI_API='https://qlaiapi.raycos.com.cn'
 VUE_APP_BASE_API = 'http://58.246.234.210:8084'
 #192.168.1.199
 

+ 1 - 1
.env.production

@@ -2,7 +2,7 @@
 ENV = 'production'
 port = 8080
 # base api
-VUE_APP_BASE_AI_API='https://ylaiapi.raycos.com.cn'
+VUE_APP_BASE_AI_API='https://qlaiapi.raycos.com.cn'
 VUE_APP_BASE_API = 'http://58.246.234.210:8084'
 #客户:58.246.234.210:8084
 #域名 docapi.shqlsy.com

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/index.html


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/css/app.d60322ec.css


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/css/chunk-03ae2181.0c3c86c9.css


+ 1 - 0
dist/static/css/chunk-1bde5712.d676c2aa.css

@@ -0,0 +1 @@
+.project-search[data-v-282d6778]{display:-webkit-box;display:-ms-flexbox;display:flex;padding:10px;font-size:12px}.listClass[data-v-282d6778]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.excel-table[data-v-282d6778]{width:100%;overflow-y:auto}.excel-table table[data-v-282d6778]{border-collapse:collapse;width:100%;margin-bottom:20px}.excel-table td[data-v-282d6778],.excel-table th[data-v-282d6778]{border:1px solid #ddd;padding:8px;text-align:left;white-space:nowrap}.excel-table th[data-v-282d6778]{background-color:#f2f2f2;font-weight:700;position:sticky;top:0;z-index:1}.excel-table tr[data-v-282d6778]:nth-child(2n){background-color:#f9f9f9}.no-data[data-v-282d6778]{font-size:18px;color:#909399;text-align:center;margin-top:50px}.pagination[data-v-282d6778]{position:fixed;bottom:0;right:51%}.movable-dialog[data-v-282d6778]{position:absolute!important}[data-v-282d6778] .el-dialog__header{cursor:move}.loading-more[data-v-282d6778]{text-align:center;padding:10px 0;color:#909399}

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/css/chunk-3777bfcd.f5399100.css


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/css/chunk-38db9d02.3abc2536.css


+ 1 - 0
dist/static/css/chunk-3ecbedba.45f99df9.css

@@ -0,0 +1 @@
+.midd-page[data-v-187ef2ab]{padding:20px}.el-icon-edit[data-v-187ef2ab]{font-size:14px;color:#409eff}[data-v-187ef2ab] .model-gen-1{background-color:#e6f3ff!important}[data-v-187ef2ab] .model-gen-1.el-table__cell{background-color:#e6f3ff!important;color:#333!important}[data-v-187ef2ab] .model-gen-2{background-color:#f0e6ff!important}[data-v-187ef2ab] .model-gen-2.el-table__cell{background-color:#f0e6ff!important;color:#333!important}.header[data-v-187ef2ab]{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin:15px 0}.h_left[data-v-187ef2ab],.header[data-v-187ef2ab]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.h_left p[data-v-187ef2ab]{margin-right:45px;margin-top:0}.search-container[data-v-187ef2ab]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.search-box[data-v-187ef2ab]{width:350px}.custom-search-input[data-v-187ef2ab] :deep(.el-input__inner){height:40px;line-height:40px;border-right:none;border-radius:4px 0 0 4px}.custom-search-input[data-v-187ef2ab] :deep(.el-input-group__append){padding:0;border:none;background-color:transparent}.custom-search-input[data-v-187ef2ab] :deep(.el-input-group__append .el-button){height:40px;padding:0 20px;border-radius:0 4px 4px 0;margin:0}.custom-search-input[data-v-187ef2ab] :deep(.el-input__inner:hover){border-color:#dcdfe6}.custom-search-input[data-v-187ef2ab] :deep(.el-input__inner:focus){border-color:#409eff}

+ 0 - 1
dist/static/css/chunk-4a670a7f.e09c1a2a.css

@@ -1 +0,0 @@
-.midd-page[data-v-779c8190]{padding:20px}.el-icon-edit[data-v-779c8190]{font-size:14px;color:#409eff}[data-v-779c8190] .model-gen-1{background-color:#e6f3ff!important}[data-v-779c8190] .model-gen-1.el-table__cell{background-color:#e6f3ff!important;color:#333!important}[data-v-779c8190] .model-gen-2{background-color:#f0e6ff!important}[data-v-779c8190] .model-gen-2.el-table__cell{background-color:#f0e6ff!important;color:#333!important}.header[data-v-779c8190]{-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;margin:15px 0}.h_left[data-v-779c8190],.header[data-v-779c8190]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.h_left p[data-v-779c8190]{margin-right:45px;margin-top:0}.search-container[data-v-779c8190]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.search-box[data-v-779c8190]{width:350px}.custom-search-input[data-v-779c8190] :deep(.el-input__inner){height:40px;line-height:40px;border-right:none;border-radius:4px 0 0 4px}.custom-search-input[data-v-779c8190] :deep(.el-input-group__append){padding:0;border:none;background-color:transparent}.custom-search-input[data-v-779c8190] :deep(.el-input-group__append .el-button){height:40px;padding:0 20px;border-radius:0 4px 4px 0;margin:0}.custom-search-input[data-v-779c8190] :deep(.el-input__inner:hover){border-color:#dcdfe6}.custom-search-input[data-v-779c8190] :deep(.el-input__inner:focus){border-color:#409eff}

+ 0 - 1
dist/static/css/chunk-6c81d65e.7487f033.css

@@ -1 +0,0 @@
-.project-search[data-v-de6f7f10]{display:-webkit-box;display:-ms-flexbox;display:flex;padding:10px;font-size:12px}.listClass[data-v-de6f7f10]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.excel-table[data-v-de6f7f10]{width:100%;overflow-y:auto}.excel-table table[data-v-de6f7f10]{border-collapse:collapse;width:100%;margin-bottom:20px}.excel-table td[data-v-de6f7f10],.excel-table th[data-v-de6f7f10]{border:1px solid #ddd;padding:8px;text-align:left;white-space:nowrap}.excel-table th[data-v-de6f7f10]{background-color:#f2f2f2;font-weight:700;position:sticky;top:0;z-index:1}.excel-table tr[data-v-de6f7f10]:nth-child(2n){background-color:#f9f9f9}.no-data[data-v-de6f7f10]{font-size:18px;color:#909399;text-align:center;margin-top:50px}.pagination[data-v-de6f7f10]{position:fixed;bottom:0;right:51%}.movable-dialog[data-v-de6f7f10]{position:absolute!important}[data-v-de6f7f10] .el-dialog__header{cursor:move}.loading-more[data-v-de6f7f10]{text-align:center;padding:10px 0;color:#909399}

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/app.5b8d40a1.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/app.981f7a47.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-00d3f6c2.09e66246.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-00d3f6c2.aa3f606c.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-03ae2181.d253277f.js


+ 0 - 0
dist/static/js/chunk-0678749f.b51280a8.js → dist/static/js/chunk-0678749f.bb74f10c.js


+ 0 - 0
dist/static/js/chunk-09f78c22.fecdee92.js → dist/static/js/chunk-09f78c22.10e2d4c9.js


+ 0 - 0
dist/static/js/chunk-0ec56e2a.1048fcde.js → dist/static/js/chunk-0ec56e2a.ab0452b7.js


+ 0 - 0
dist/static/js/chunk-10b289e2.45bfad1a.js → dist/static/js/chunk-10b289e2.6be4e4a1.js


+ 0 - 0
dist/static/js/chunk-11ff1a32.cd040f0a.js → dist/static/js/chunk-11ff1a32.61a74561.js


+ 0 - 0
dist/static/js/chunk-138f7583.bbaf1522.js → dist/static/js/chunk-138f7583.43c23be9.js


+ 0 - 0
dist/static/js/chunk-16a60d86.607eb3dc.js → dist/static/js/chunk-16a60d86.90d8f25a.js


+ 0 - 0
dist/static/js/chunk-194e2959.a7579d68.js → dist/static/js/chunk-194e2959.d01d3364.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-1bde5712.06261586.js


+ 0 - 0
dist/static/js/chunk-23d4d74d.5a6e9090.js → dist/static/js/chunk-23d4d74d.2794e8b8.js


+ 0 - 0
dist/static/js/chunk-2d0d3705.965d98b8.js → dist/static/js/chunk-2d0d3705.8eb62e74.js


+ 0 - 0
dist/static/js/chunk-35464144.777f50a8.js → dist/static/js/chunk-35464144.15682d7b.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-3777bfcd.a0fbf115.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-38db9d02.1d4aee9b.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-3ecbedba.fb9abb66.js


+ 0 - 0
dist/static/js/chunk-44eee83d.9a2fec1c.js → dist/static/js/chunk-44eee83d.9d235966.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-4a670a7f.e5cc1146.js


+ 0 - 0
dist/static/js/chunk-4ac7f82e.1ac615ea.js → dist/static/js/chunk-4ac7f82e.ef61581d.js


+ 0 - 0
dist/static/js/chunk-52f685c2.dd0395c3.js → dist/static/js/chunk-52f685c2.bb5a2f67.js


+ 0 - 0
dist/static/js/chunk-5a3bdf57.e59c280b.js → dist/static/js/chunk-5a3bdf57.ef1ef08f.js


+ 0 - 0
dist/static/js/chunk-616f7602.8e8d1779.js → dist/static/js/chunk-616f7602.63185d7a.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-6711f3a4.253a954d.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-6711f3a4.28993305.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-674d021c.3502f874.js


+ 0 - 0
dist/static/js/chunk-683feb49.c0ab1f1a.js → dist/static/js/chunk-683feb49.ab95f8ec.js


+ 0 - 0
dist/static/js/chunk-6a80e4ca.52258a74.js → dist/static/js/chunk-6a80e4ca.c571b091.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-6c81d65e.10bd263e.js


+ 0 - 0
dist/static/js/chunk-6ddf6871.385bc023.js → dist/static/js/chunk-6ddf6871.ea368fb3.js


+ 0 - 0
dist/static/js/chunk-708604c0.0abdf652.js → dist/static/js/chunk-708604c0.6338ce53.js


+ 0 - 0
dist/static/js/chunk-7150dcbc.025c50f9.js → dist/static/js/chunk-7150dcbc.7adf2888.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-77e7ad00.4552a5d5.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-77e7ad00.6c1dc206.js


+ 0 - 0
dist/static/js/chunk-7a88741c.80fb1863.js → dist/static/js/chunk-7a88741c.70c1d7e3.js


+ 0 - 0
dist/static/js/chunk-9235c1ae.33d9ebb2.js → dist/static/js/chunk-9235c1ae.7740a205.js


La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 0 - 0
dist/static/js/chunk-libs.188d12a6.js


+ 10 - 0
src/api/knowledge.js

@@ -761,4 +761,14 @@ export function endClear(data) {
     method: 'post',
     data
   })
+}
+
+/* 获取SOTA数据  */
+
+export function getSotaDetails(data) {
+  return request({
+    url: 'sota-details/search',
+    method: 'post',
+    data
+  })
 }

+ 4 - 2
src/components/CanvasEditor/index.vue

@@ -2143,6 +2143,8 @@ export default {
 /*  @import url("./styles.scss");  */
 .canvas-editor-wrapper {
   position: relative;
+  height: 100%; // 添加固定高度
+  overflow: hidden; // 防止内容溢出
   .canvas-container {
     .footer {
       position: static;
@@ -2161,9 +2163,9 @@ export default {
       position: static;
       display: flex;
       flex-direction: column;
-      height: calc(100% - 90px); /* 减去header和footer的高度 */
+      height: 700px;//calc(100% - 90px); /* 减去header和footer的高度 */
       background-color: #f2f4f7;
-      overflow-y: auto; /* 仅让Main区域可滚动 */
+      overflow: auto; /* 仅让Main区域可滚动 */
       flex-grow: 1; /* 让Main区域占据剩余空间 */
     }
     @media (max-width: 1220px) {

+ 5 - 5
src/components/FilePreview/index.vue

@@ -607,10 +607,10 @@ export default {
         }
 
         // 正确获取起始点和终点坐标
-        const startX = coords[2]; // x1
-        const endY = coords[0]; // y1
-        const endX = coords[3]; // x2
-        const startY = coords[1]; // y2
+        const startX = coords[0]; // x1
+        const endY = coords[1]; // y1
+        const endX = coords[2]; // x2
+        const startY = coords[3]; // y2
 
         // 计算矩形的位置和尺寸
         const highlight = {
@@ -688,7 +688,7 @@ export default {
   <style scoped>
 .file-preview {
   width: 100%;
-  height: 100vh;
+  /* height: 100vh; */
   overflow: hidden;
   display: flex;
   flex-direction: column;

+ 279 - 13
src/components/ai.vue

@@ -43,7 +43,7 @@
           </li>
         </ul>
       </div>
-      <div class="input-container">
+      <!--  <div class="input-container">
         <textarea
           v-model="userInput"
           rows="4"
@@ -51,6 +51,79 @@
           placeholder="发送消息..."
         ></textarea>
         <button @click="sendMessage" class="btn">发送</button>
+      </div> -->
+      <div class="input-container">
+        <!-- 文件上传预览区 -->
+        <div
+          class="upload-preview"
+          v-if="uploadFiles.length > 0 || documents.length > 0"
+        >
+          <div
+            v-for="(file, index) in uploadFiles"
+            :key="'img-' + index"
+            class="preview-item"
+          >
+            <div class="preview-content">
+              <img
+                v-if="file.type.startsWith('image/')"
+                :src="file.preview"
+                class="preview-image"
+                alt="preview"
+              />
+              <div v-else class="file-info">
+                <i class="el-icon-document"></i>
+                <span>{{ file.name }}</span>
+              </div>
+            </div>
+            <i class="el-icon-close remove-file" @click="removeFile(index)"></i>
+          </div>
+
+          <div
+            v-for="(doc, index) in documents"
+            :key="'doc-' + index"
+            class="preview-item document-item"
+          >
+            <div class="preview-content">
+              <div class="file-info">
+                <i class="el-icon-document"></i>
+                <span>{{ doc.name }}</span>
+              </div>
+            </div>
+            <i
+              class="el-icon-close remove-file"
+              @click="removeDocument(index)"
+            ></i>
+          </div>
+        </div>
+
+        <!-- 输入区域 -->
+        <div class="input-area">
+          <textarea
+            v-model="userInput"
+            rows="4"
+            @keydown="handleKeydown"
+            placeholder="发送消息..."
+          ></textarea>
+
+          <!-- 工具栏 -->
+          <div class="toolbar">
+            <div class="left-tools">
+              <input
+                type="file"
+                ref="fileInput"
+                multiple
+                @change="handleFileUpload"
+                accept="image/*,.pdf,.doc,.docx,.txt"
+                style="display: none"
+              />
+              <button class="tool-btn" @click="$refs.fileInput.click()">
+                <i class="el-icon-upload2"></i>
+              </button>
+            </div>
+            <!--  <button @click="playAudio" class="btn">播放音频</button> -->
+            <button @click="sendMessage" class="btn">发送</button>
+          </div>
+        </div>
       </div>
       <!-- </div> -->
     </div>
@@ -68,7 +141,7 @@ import LoginModal from "@/components/LoginModal";
 import MarkdownIt from "markdown-it";
 import { pcInnerAi, getMinioURl } from "@/api/api";
 import { modelList, get_default } from "@/api/knowledge";
-import axios from 'axios';
+import axios from "axios";
 const md = new MarkdownIt();
 
 export default {
@@ -112,6 +185,8 @@ export default {
         document: "",
       },
       currentChat_id: "",
+      uploadFiles: [], // 存储上传的文件
+      documents: [], // 新增:专门存储文档类型文件的数组
     };
   },
   mounted() {
@@ -147,6 +222,64 @@ export default {
     this.init();
   },
   methods: {
+    /* 播放音频 */
+    playAudio(audioBase64) {
+      console.log(audioBase64);
+      // 创建音频元素
+      const audio = new Audio(audioBase64); // 播放音频
+      audio.play().catch((error) => {
+        console.error("Error playing audio:", error);
+      });
+    },
+    async handleFileUpload(event) {
+      const files = Array.from(event.target.files);
+
+      for (const file of files) {
+        try {
+          const formData = new FormData();
+          formData.append("file", file);
+
+          const response = await axios.post(
+            `${process.env.VUE_APP_BASE_AI_API}/upload/file`, //process.env.VUE_APP_BASE_API
+            formData,
+            {
+              headers: {
+                "Content-Type": "multipart/form-data",
+              },
+            }
+          );
+
+          const fileUrl = response.data.data.fileUrl;
+          const fileInfo = {
+            name: file.name,
+            type: file.type,
+            preview: fileUrl,
+            url: fileUrl,
+          };
+
+          // 根据文件类型分别存储
+          if (file.type.startsWith("image/")) {
+            this.uploadFiles.push(fileInfo);
+          } else {
+            // 文档类型:pdf, doc, docx, txt 等
+            this.documents.push(fileInfo);
+          }
+        } catch (error) {
+          console.error("文件上传失败:", error);
+          this.$message.error(`文件 ${file.name} 上传失败`);
+        }
+      }
+
+      event.target.value = "";
+    },
+
+    removeFile(index) {
+      this.uploadFiles.splice(index, 1);
+    },
+    removeDocument(index) {
+      this.documents.splice(index, 1);
+    },
+    /*  */
     checkAIData() {
       const aiData = localStorage.getItem("AIData");
       this.showLoginModal = !aiData;
@@ -280,6 +413,12 @@ export default {
       }
       const chatId = this.getUuid();
       let message = this.userInput.trim();
+      const imageUrls = this.uploadFiles
+        .filter((file) => file.type.startsWith("image/"))
+        .map((file) => file.url);
+
+      // 获取文档 URL
+      const documentUrls = this.documents.map((doc) => doc.url);
       if (!message) return;
 
       // 添加用户消息
@@ -291,7 +430,8 @@ export default {
         done: true,
       });
       this.userInput = "";
-
+      this.uploadFiles = [];
+      this.documents = [];
       // 添加机器人的响应消息
       const botMessage = {
         user: "bot",
@@ -312,13 +452,18 @@ export default {
 
       try {
         // 直接使用 axios 而不是 this.$axios
-        const response = await axios.post(`${process.env.VUE_APP_BASE_API}/chatbot/chat`, {
-          message: message,
-          chat_config_id: this.currentChat_id,
-          user_id: this.$store.state.user.id,
-          session_id: this.session_id || "",
-          source: "pc",
-        });
+        const response = await axios.post(
+          `${process.env.VUE_APP_BASE_AI_API}/chatbot/multimodal`,
+          {
+            message: message,
+            chat_config_id: this.currentChat_id,
+            user_id: this.$store.state.user.id,
+            session_id: this.session_id || "",
+            source: "pc",
+            image_urls: imageUrls,
+            documents: documentUrls, // 添加文档 URLs
+          }
+        );
 
         // 停止思考动画
         thinkingController.abort();
@@ -327,14 +472,20 @@ export default {
         if (response.status === 200) {
           const result = response.data;
           this.session_id = result.data.session_id;
-          
+
           if (this.$route.name !== "ai") {
             const additionalInput = result.data.milvus_ids;
             this.idArray = additionalInput;
             const idObject = { ids: this.idArray };
             await this.getMinioUrls(idObject);
           }
-
+          // 如果有音频,播放音频
+          if (
+            response.data.data.audio_info &&
+            response.data.data.audio_info.audio
+          ) {
+            this.playAudio(response.data.data.audio_info.audio);
+          }
           this.handleResponse(result, botMessage);
         } else {
           throw new Error(`HTTP error! status: ${response.status}`);
@@ -354,7 +505,7 @@ export default {
         console.log("Sending to backend:", JSON.stringify(idObject));
 
         const response = await axios.post(
-          `${process.env.VUE_APP_BASE_API}/milvus/getMinioURl`,
+          `${process.env.VUE_APP_BASE_AI_API}/milvus/getMinioURl`,
           idObject,
           {
             headers: {
@@ -910,4 +1061,119 @@ textarea {
     margin: 0 auto; /* 居中显示 */
   }
 }
+
+/* 新输入框 */
+.upload-preview {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  max-height: 100px;
+  overflow-y: auto;
+}
+
+.preview-item {
+  position: relative;
+  width: 60px;
+  height: 20px;
+  border-radius: 4px;
+  overflow: hidden;
+  border: 1px solid #e0e0e0;
+}
+
+.preview-content {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.preview-image {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+.file-info {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  height: 100%;
+  padding: 5px;
+  text-align: center;
+}
+
+.file-info span {
+  font-size: 12px;
+  word-break: break-word;
+  overflow: hidden;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+}
+.remove-file {
+  position: absolute;
+  top: 2px;
+  right: 2px;
+  background: rgba(0, 0, 0, 0.5);
+  color: white;
+  border-radius: 50%;
+  padding: 2px;
+  cursor: pointer;
+  font-size: 12px;
+}
+
+.input-area {
+  width: 100%;
+}
+
+.toolbar {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-top: 10px;
+}
+
+.left-tools {
+  display: flex;
+  gap: 10px;
+}
+
+.tool-btn {
+  background: none;
+  border: none;
+  cursor: pointer;
+  padding: 5px;
+  color: #666;
+  font-size: 20px;
+}
+
+.tool-btn:hover {
+  color: #3cbade;
+}
+
+.document-item {
+  width: 120px; /* 文档预览项可以设置得更宽一些 */
+  height: 60px;
+  background-color: #f5f5f5;
+}
+
+.document-item .file-info {
+  display: flex;
+  align-items: center;
+  gap: 5px;
+}
+
+.document-item .file-info i {
+  font-size: 20px;
+  color: #666;
+}
+
+.document-item .file-info span {
+  font-size: 12px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
 </style>

+ 3 - 3
src/components/chartIcon/index.vue

@@ -31,7 +31,7 @@ export default {
     return {
       showChat: false,
       chatWidth: 500,
-      chatHeight: 625,
+      chatHeight: 735,
       isResizing: false,
       startX: 0,
       startY: 0,
@@ -48,7 +48,7 @@ export default {
     },
     messagesHeight() {
       // 减去其他元素的高度(如头部、输入框等)
-      return `calc(${this.chatHeight}px - 195px)`;
+      return `calc(${this.chatHeight}px - 255px)`;
     },
   },
   watch: {
@@ -107,7 +107,7 @@ export default {
         const messagesEl = chatBox.$el.querySelector('.messages');
         if (messagesEl) {
           const chatWindowHeight = this.$refs.chatWindow.offsetHeight;
-          const otherElementsHeight = 195; // Adjust this value as needed
+          const otherElementsHeight = 255; // Adjust this value as needed
           messagesEl.style.height = `${chatWindowHeight - otherElementsHeight}px`;
         }
       }

+ 15 - 9
src/views/Information/index.vue

@@ -127,15 +127,17 @@
       :close-on-click-modal="false"
       width="80%"
     >
-      <CanvasEditor
-        ref="wordEditor"
-        :edit-mode="Modetype"
-        :html-data="htmlData"
-        :doc-json="docJson"
-        :key="keys"
-        @save="save"
-        @is-save="getSave"
-      ></CanvasEditor>
+      <div style="overflow: auto">
+        <CanvasEditor
+          ref="wordEditor"
+          :edit-mode="Modetype"
+          :html-data="htmlData"
+          :doc-json="docJson"
+          :key="keys"
+          @save="save"
+          @is-save="getSave"
+        ></CanvasEditor>
+      </div>
       <!--   </el-form-item>
         </el-form> -->
       <div slot="footer">
@@ -432,4 +434,8 @@ export default {
     max-width: 430px; // 可以根据需要调整最大宽度
   }
 }
+/* 添加以下样式 */
+::v-deep .el-dialog {
+  margin-top: 0 !important;
+}
 </style>

+ 19 - 0
src/views/knowledgeMenu/category/components/dataSearch.vue

@@ -164,6 +164,11 @@ export default {
         };
       },
     },
+    currentFolderId: {
+      // 添加新的 prop 接收当前选中的目录 ID
+      type: [String, Number],
+      default: "",
+    },
   },
   data() {
     // 自定义验证函数
@@ -270,6 +275,20 @@ export default {
       folderList: [], // 确保这个字段存在
     };
   },
+  watch: {
+    currentFolderId: {
+      handler(newVal) {
+        // 确保类型转换正确处理
+        if (newVal === "001" || newVal === "") {
+          this.uploadForm.doc_type_id = "";
+        } else {
+          // 将值转换为数字类型
+          this.uploadForm.doc_type_id = Number(newVal) || "";
+        }
+      },
+      immediate: true,
+    },
+  },
   mounted() {
     this.url = process.env.VUE_APP_BASE_API;
     this.queryForm.bucket_id = this.$route.query.id;

+ 5 - 3
src/views/knowledgeMenu/category/infoList.vue

@@ -25,12 +25,14 @@
             padding: 10px 10px 10px 0;
             cursor: pointer;
           "
-        /> -->
+        />   v-html="item.content"-->
         <div
           style="margin-bottom: 20px"
-          v-html="item.content"
           @click="scrollToPage(item.page_number, item.coordinate)"
-        ></div>
+        >
+        <span v-if="item.url==''||item.url==null">{{ item.content }}</span>
+          <img v-else :src="item.url" alt="" />
+        </div>
       </div>
       <el-dialog
         :visible.sync="imagePreviewVisible"

+ 6 - 2
src/views/knowledgeMenu/category/knowledgeSet.vue

@@ -52,6 +52,7 @@
       <dataSearch
         @bindSetQuery="setQuery"
         @refreshFolders="typeList"
+        :currentFolderId="queryForm.doc_type_id"
       ></dataSearch>
       <!--  <dataList
         :queryForm="queryForm"
@@ -1030,7 +1031,9 @@ export default {
             .then((res) => {
               if (res.status === 200) {
                 this.$message.success("批量解析任务已提交");
-                this.updateAnalysisStatus(selectedIds);
+                setTimeout(() => {
+                  this.updateAnalysisStatus(selectedIds);
+                }, 5000);
                 this.clearSelection();
               } else {
                 this.$message.error("批量解析请求失败");
@@ -1140,10 +1143,11 @@ export default {
       analysisPro(analysisConfig)
         .then((res) => {
           if (res.status === 200) {
+            this.$set(row, "progress", 0);
             // Start checking the status immediately after successful API call
             setTimeout(() => {
               this.startAnalysisStatusChecker(row);
-            });
+            }, 5000);
           } else {
             this.$message.error("解析请求失败");
             this.$set(row, "run", 0);

+ 101 - 58
src/views/login/aiIndex.vue

@@ -54,10 +54,10 @@
       </div> -->
       <div class="input-container">
         <!-- 文件上传预览区 -->
-        <div class="upload-preview" v-if="uploadFiles.length > 0">
+        <div class="upload-preview" v-if="uploadFiles.length > 0 || documents.length > 0">
           <div
             v-for="(file, index) in uploadFiles"
-            :key="index"
+            :key="'img-'+index"
             class="preview-item"
           >
             <div class="preview-content">
@@ -74,6 +74,20 @@
             </div>
             <i class="el-icon-close remove-file" @click="removeFile(index)"></i>
           </div>
+
+          <div
+            v-for="(doc, index) in documents"
+            :key="'doc-'+index"
+            class="preview-item document-item"
+          >
+            <div class="preview-content">
+              <div class="file-info">
+                <i class="el-icon-document"></i>
+                <span>{{ doc.name }}</span>
+              </div>
+            </div>
+            <i class="el-icon-close remove-file" @click="removeDocument(index)"></i>
+          </div>
         </div>
 
         <!-- 输入区域 -->
@@ -100,6 +114,7 @@
                 <i class="el-icon-upload2"></i>
               </button>
             </div>
+           <!--  <button @click="playAudio" class="btn">播放音频</button> -->
             <button @click="sendMessage" class="btn">发送</button>
           </div>
         </div>
@@ -166,6 +181,7 @@ export default {
       currentChat_id: "",
       phone: "",
       uploadFiles: [], // 存储上传的文件
+      documents: [], // 新增:专门存储文档类型文件的数组
     };
   },
   mounted() {
@@ -204,41 +220,25 @@ export default {
     this.init();
   },
   methods: {
+    /* 播放音频 */
+    playAudio(audioBase64) {
+      console.log(audioBase64);
+      // 创建音频元素
+      const audio = new Audio(audioBase64); // 播放音频
+      audio.play().catch((error) => {
+        console.error("Error playing audio:", error);
+      });
+    },
     async handleFileUpload(event) {
       const files = Array.from(event.target.files);
 
-      // 检查当前已上传的图片数量
-      const currentImageCount = this.uploadFiles.filter((file) =>
-        file.type.startsWith("image/")
-      ).length;
-
-      // 计算还能上传多少张图片
-      const remainingSlots = 5 - currentImageCount;
-
-      if (remainingSlots <= 0) {
-        this.$message.warning("最多只能上传5张图片");
-        event.target.value = "";
-        return;
-      }
-
-      // 只处理允许的数量的文件
-      const filesToProcess = files.slice(0, remainingSlots);
-
-      if (files.length > remainingSlots) {
-        this.$message.warning(
-          `已达到上传上限,只能再上传${remainingSlots}张图片`
-        );
-      }
-
-      for (const file of filesToProcess) {
+      for (const file of files) {
         try {
-          // 创建 FormData 对象
           const formData = new FormData();
           formData.append("file", file);
 
-          // 发送上传请求
           const response = await axios.post(
-            `${process.env.VUE_APP_BASE_API}/upload/file`,
+            `${process.env.VUE_APP_BASE_AI_API}/upload/file`,//process.env.VUE_APP_BASE_API
             formData,
             {
               headers: {
@@ -247,28 +247,36 @@ export default {
             }
           );
 
-          // 获取接口返回的文件URL
           const fileUrl = response.data.data.fileUrl;
-          // 将文件信息添加到 uploadFiles 数组
-          this.uploadFiles.push({
+          const fileInfo = {
             name: file.name,
             type: file.type,
             preview: fileUrl,
             url: fileUrl,
-          });
+          };
+
+          // 根据文件类型分别存储
+          if (file.type.startsWith('image/')) {
+            this.uploadFiles.push(fileInfo);
+          } else {
+            // 文档类型:pdf, doc, docx, txt 等
+            this.documents.push(fileInfo);
+          }
         } catch (error) {
           console.error("文件上传失败:", error);
           this.$message.error(`文件 ${file.name} 上传失败`);
         }
       }
 
-      // 清空input以允许重复上传相同文件
       event.target.value = "";
     },
 
     removeFile(index) {
       this.uploadFiles.splice(index, 1);
     },
+    removeDocument(index) {
+      this.documents.splice(index, 1);
+    },
     /*  */
     checkAIData() {
       const aiData = localStorage.getItem("AIData");
@@ -368,7 +376,7 @@ export default {
         console.log("WebSocket 连接已建立:", event);
       };
       this.websocket.onmessage = (event) => {
-        // 解收到的消息
+        // 解����收到的消息
         const result = JSON.parse(event.data);
 
         // 检查消息是否完结
@@ -402,14 +410,17 @@ export default {
         }
       }
       const chatId = this.getUuid();
-      const wsUrl = `${process.env.VUE_APP_BASE_API}/chatbot/multimodal`;
+      const wsUrl = `${process.env.VUE_APP_BASE_AI_API}/chatbot/multimodal`; //process.env.VUE_APP_BASE_AI_API /chatbot/multimodal /api/chat/chatgpt
       let message = this.userInput.trim();
-      
+
       const imageUrls = this.uploadFiles
         .filter((file) => file.type.startsWith("image/"))
         .map((file) => file.url);
 
-      if (message || imageUrls.length > 0) {
+      // 获取文档 URL
+      const documentUrls = this.documents.map(doc => doc.url);
+
+      if (message || imageUrls.length > 0 || documentUrls.length > 0) {
         message = message.replace(/(\r\n|\r|\n)/g, "  \n");
 
         this.messages.push({
@@ -422,6 +433,7 @@ export default {
 
         this.userInput = "";
         this.uploadFiles = [];
+        this.documents = [];
 
         const botMessage = {
           user: "bot",
@@ -441,23 +453,26 @@ export default {
 
         try {
           const response = await axios({
-            method: 'post',
+            method: "post",
             url: wsUrl,
             data: {
               message: message,
               chat_config_id: "17",
               user_id: this.phone,
               session_id: this.session_id || "",
-              source: 'pc',
-              image_url: imageUrls,
-              history_limit: 10
-            }
+              source: "pc",
+              image_urls: imageUrls,
+              documents: documentUrls, // 添加文档 URLs
+              //history_limit: 10
+            },
           });
 
           this.session_id = response.data.data.session_id;
-          
+
           if (response.data.code !== 200) {
-            throw new Error(`HTTP error! status: ${response.status}, message: ${response.data.message}`);
+            throw new Error(
+              `HTTP error! status: ${response.status}, message: ${response.data.message}`
+            );
           }
 
           thinkingController.abort();
@@ -470,16 +485,21 @@ export default {
             console.log("ID Object:", idObject);
             await this.getMinioUrls(idObject);
           }
-
+          // 如果有音频,播放音频
+          if (
+            response.data.data.audio_info &&
+            response.data.data.audio_info.audio
+          ) {
+            this.playAudio(response.data.data.audio_info.audio);
+          }
           this.handleResponse(response.data, botMessage);
-          
         } catch (error) {
           console.error("Error sending message:", error);
           thinkingController.abort();
           await thinkingPromise;
           botMessage.done = true;
           botMessage.message = "抱歉,发生了错误,请稍后再试。";
-          
+
           if (error.response) {
             console.error("Response data:", error.response.data);
             console.error("Response status:", error.response.status);
@@ -498,7 +518,7 @@ export default {
         console.log("Sending to backend:", JSON.stringify(idObject));
 
         const response = await axios.post(
-          "http://58.246.234.210:8084/milvus/getMinioURl",
+          `${process.env.VUE_APP_BASE_AI_API}/milvus/getMinioURl`,
           idObject,
           {
             headers: {
@@ -560,7 +580,7 @@ export default {
     },
 
     async handleResponse(value, existingMessage) {
-      const data = value.data.answer.answer;
+      const data = value.data.answer;
 
       existingMessage.messageType = data;
       /* existingMessage.time = data.timestamp; */
@@ -581,7 +601,7 @@ export default {
         });
         sourceText += "</ol></div>";
       }
-
+      console.log(mainText);
       // 先进行主要内容的打字效果
       await this.typeMessage(existingMessage, mainText);
 
@@ -618,7 +638,7 @@ export default {
         linkify: true,
       });
 
-      // 自定义链接渲染规
+      // 自定义链接渲染规���
       md.renderer.rules.link_open = (tokens, idx, options, env, self) => {
         const token = tokens[idx];
         const hrefIndex = token.attrIndex("href");
@@ -959,7 +979,7 @@ export default {
 .message-text {
   margin: 0;
   white-space: normal; /* 确保文本会换行 */
-  word-wrap: break-word; /* 长单词或 URL 会被断开适应容器宽度 */
+  word-wrap: break-word; /* 长单词或 URL 会被断开���适应容器宽度 */
   overflow: auto;
   font-size: 14px;
 }
@@ -992,7 +1012,7 @@ export default {
   left: 50%;
   transform: translateX(-50%);
   width: 90%; /* 设置宽度为弹窗宽度的90% */
-  max-width: 800px; /* 设置最大宽度 */
+  max-width: 1170px; /* 设置最大宽度 */
   display: flex;
   justify-content: space-between;
   align-items: flex-start;
@@ -1017,7 +1037,7 @@ textarea {
 .input-container textarea {
   width: 100%; /* 输入框占据所有可用空间 */
   padding: 10px;
-  margin-right: 10px; /* 与按钮的间 */
+  margin-right: 10px; /* 与按钮的间�� */
   box-sizing: border-box;
 }
 
@@ -1060,15 +1080,14 @@ textarea {
   display: flex;
   flex-wrap: wrap;
   gap: 10px;
-  margin-bottom: 10px;
   max-height: 100px;
   overflow-y: auto;
 }
 
 .preview-item {
   position: relative;
-  width: 80px;
-  height: 80px;
+  width: 60px;
+  height: 20px;
   border-radius: 4px;
   overflow: hidden;
   border: 1px solid #e0e0e0;
@@ -1146,4 +1165,28 @@ textarea {
 .tool-btn:hover {
   color: #3cbade;
 }
+
+.document-item {
+  width: 120px; /* 文档预览项可以设置得更宽一些 */
+  height: 60px;
+  background-color: #f5f5f5;
+}
+
+.document-item .file-info {
+  display: flex;
+  align-items: center;
+  gap: 5px;
+}
+
+.document-item .file-info i {
+  font-size: 20px;
+  color: #666;
+}
+
+.document-item .file-info span {
+  font-size: 12px;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
 </style>

+ 24 - 10
src/views/middPage/index.vue

@@ -27,6 +27,7 @@
         </p>
       </div>
     </div>
+    <div><el-button @click="handlePreview">预览</el-button></div>
     <el-table :data="firstTableData" style="width: 100%">
       <el-table-column
         label="项目"
@@ -248,8 +249,8 @@
       title="配置模型"
       width="40%"
     >
-     <el-form :model="firstForm.configForm" label-width="120px">
-         <!-- <el-form-item label="模型1">
+      <el-form :model="firstForm.configForm" label-width="120px">
+        <!-- <el-form-item label="模型1">
           <el-select
             v-model="firstForm.configForm.model_a_config"
             placeholder="请选择应用"
@@ -525,14 +526,16 @@ export default {
   },
   computed: {
     firstTableData() {
-      return this.firstForm.tableData.filter((data) => {
-        return (
-          (!this.firstForm.activeFilters.workType.length ||
-            this.firstForm.activeFilters.workType.includes(data.workType)) &&
-          (!this.firstForm.activeFilters.project.length ||
-            this.firstForm.activeFilters.project.includes(data.project))
-        );
-      }).reverse();
+      return this.firstForm.tableData
+        .filter((data) => {
+          return (
+            (!this.firstForm.activeFilters.workType.length ||
+              this.firstForm.activeFilters.workType.includes(data.workType)) &&
+            (!this.firstForm.activeFilters.project.length ||
+              this.firstForm.activeFilters.project.includes(data.project))
+          );
+        })
+        .reverse();
     },
   },
   mounted() {
@@ -543,6 +546,15 @@ export default {
     this.updateProjectFilters();
   },
   methods: {
+    /* 预览 */
+    handlePreview(row) {
+      console.log(row);
+      let _this = this;
+      let a = document.createElement("a");
+      a.href = `#/infoList?id=${this.$route.query.id}&type=${this.$route.query.type.toLowerCase()}`; // 使用 hash
+      a.target = "_blank";
+      a.click();
+    },
     /* 说明编辑 */
     openDescriptionDialog(row) {
       this.descriptionDialogVisible = true;
@@ -587,12 +599,14 @@ export default {
         saveConfig(this.firstForm.configForm).then((res) => {
           if (res.status !== 200) return;
           this.firstForm.configVisible = false;
+          this.firstInit(); // 刷新列表数据
         });
       } else {
         updateConfig(this.firstForm.configForm).then((res) => {
           if (res.status !== 200) return;
           this.firstForm.configVisible = false;
           this.$message.success("修改成功!");
+          this.firstInit(); // 刷新列表数据
         });
       }
     },

+ 263 - 132
src/views/middPage/list.vue

@@ -109,7 +109,7 @@
                 type="daterange"
                 range-separator="至"
                 start-placeholder="开始日期"
-                end-placeholder="结束日"
+                end-placeholder="结束日��"
                 value-format="yyyy-MM-dd"
               />
             </el-form-item> -->
@@ -235,66 +235,27 @@
           />
           <el-table-column prop="status" label="处理状态" width="100">
             <template #default="{ row }">
-              <!--   <el-tooltip
-                effect="dark"
-                placement="top"
-                :disabled="
-                  Number(row.processing_status) !== 1 &&
-                  Number(row.processing_status) !== 5 &&
-                  Number(row.processing_status) !== 4
-                "
-              >
-                <template #content>
-                  <div style="padding: 5px">
-                    <div style="margin-top: 5px; font-size: 12px">
-                      {{ row.progress_msg || "正在处理中..." }}
-                    </div>
-                  </div>
-                </template>
-                <div class="status-wrapper">
-                  <template v-if="Number(row.processing_status) === 1 || Number(row.processing_status) === 5">
-                    <div class="progress-wrapper">
-                      <el-progress
-                        type="circle"
-                        :percentage="Math.floor(row.progress) || 0"
-                        :width="35"
-                        :stroke-width="4"
-                        :format="percentFormat"
-                        
-                      ></el-progress>
-                    </div>
-                  </template>
-                  <template v-else>
-                    <el-tag :type="getStatusType(row.processing_status)">
-                      {{ getStatusText(row.processing_status) }}
-                    </el-tag>
-                  </template>
-                </div>
-              </el-tooltip> -->
               <el-tooltip
                 effect="dark"
                 placement="top"
-                :disabled="
-                  Number(row.processing_status) !== 1 &&
-                  Number(row.processing_status) !== 5 &&
-                  Number(row.processing_status) !== 4
-                "
+                :disabled="![1, 3, 5].includes(Number(row.processing_status))"
               >
                 <template #content>
                   <div style="padding: 5px">
                     <div style="margin-top: 5px; font-size: 12px">
-                      {{ row.progress_msg || "正在处理中..." }}
+                      {{
+                        row.progress_msg ||
+                        (row.processing_status === 3
+                          ? "处理失败"
+                          : "正在处理中...")
+                      }}
                     </div>
                   </div>
                 </template>
                 <div class="status-wrapper">
                   <template
-                    v-if="
-                      Number(row.processing_status) === 1 ||
-                      Number(row.processing_status) === 5
-                    "
+                    v-if="[1, 5].includes(Number(row.processing_status))"
                   >
-                    <!-- 移除进度条,改为显示文本 -->
                     <el-tag type="warning">处理中</el-tag>
                   </template>
                   <template v-else>
@@ -368,26 +329,70 @@
       </el-tab-pane>
 
       <!-- 添加 SOTA详情 tab -->
-      <el-tab-pane label="SOTA详情" name="second">
+      <el-tab-pane label="Outcomes详情" name="second">
+        <!-- 添加搜索表单 -->
+        <div class="search-form">
+          <el-form
+            :inline="true"
+            :model="Sota_searchForm"
+            class="demo-form-inline"
+            label-width="80px"
+            ><el-form-item label="内容:">
+              <el-input
+                style="width: 260px"
+                v-model="Sota_searchForm.keyword"
+                clearable
+                placeholder="请输入文献名称\ID"
+              ></el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" @click="handleSotaSearch"
+                >搜索</el-button
+              >
+              <el-button @click="resetSotaSearch" type="danger">重置</el-button>
+              <el-button @click="outExport" type="primary">导出</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
         <el-table :data="sotaDetailsList" style="width: 100%">
-          <el-table-column label="Identifier" prop="identifier" />
-          <el-table-column label="Device" prop="device" />
-          <el-table-column label="Outcomes" prop="outcomes" />
-          <el-table-column label="Unit for analysis" prop="unit" />
-          <el-table-column label="Mean" prop="mean" />
+          <el-table-column label="文献ID" prop="literature_id" />
+          <el-table-column label="文献名称" prop="literature_name" />
+          <!--   <el-table-column label="Identifier" prop="identifier" /> -->
+          <el-table-column label="Outcomes Name" prop="outcome_name" />
+          <el-table-column
+            label="Outcome Definition"
+            prop="outcome_definition"
+          ></el-table-column>
+          <el-table-column label="Group" prop="group"></el-table-column>
+          <el-table-column label="Patient" prop="patient" />
+          <el-table-column label="Outcomes Value" prop="value" />
+          <!-- <el-table-column label="Unit for analysis" prop="unit" /> -->
+          <el-table-column label="创建时间" prop="create_time" />
           <!-- 操作列与原有类似 -->
         </el-table>
+
+        <!-- 分页器 -->
+        <el-pagination
+          class="pagination"
+          @size-change="handleStoaSizeChange"
+          @current-change="handleStoaCurrentChange"
+          :current-page="Sota_searchForm.page"
+          :page-sizes="[10, 20, 50, 100]"
+          :page-size="Sota_searchForm.page_size"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="Stoa_total"
+        />
       </el-tab-pane>
 
       <!-- 添加 SOTA汇总表 tab -->
-      <el-tab-pane label="SOTA汇总表" name="third">
+      <!-- <el-tab-pane label="SOTA汇总表" name="third">
         <el-table :data="sotaSummaryList" style="width: 100%">
           <el-table-column label="Summary" prop="summary" />
           <el-table-column label="EqMD" prop="eqmd" />
           <el-table-column label="Pain:VAS" prop="painVas" />
-          <!-- 操作列与原有类似 -->
+        
         </el-table>
-      </el-tab-pane>
+      </el-tab-pane> -->
     </el-tabs>
 
     <!-- 添加标签对话框 -->
@@ -460,6 +465,8 @@ import {
   process_model_regeneration,
   categories,
   literResult,
+  getSotaDetails,
+  exportOutcoms
 } from "@/api/knowledge";
 import axios from "axios";
 // 状态映射表
@@ -517,18 +524,26 @@ export default {
         1: { text: "处理中", type: "warning" },
         2: { text: "处理完成", type: "success" },
         3: { text: "处理失败", type: "danger" },
-        4: { text: "处理失败", type: "danger" },
         5: { text: "待处理", type: "warning" },
       },
       multipleSelection: [], // 存储跨页选中的数据
       selectedRows: [], // 当前页选中的行
       allSelectedRows: new Map(), // 存储所有选中的行
       isAllDataSelected: false, // 是否选中所有数据
+
+      /* SOTA搜索 */
+      Sota_searchForm: {
+        page: 1,
+        page_size: 10,
+        keyword: "",
+      },
+      /* SOTA详情 总数 */
+      Stoa_total: 0,
     };
   },
 
   created() {
-    // 使用防抖处理数据加载
+    // 使用抖处理数据加载
     this.debouncedFetchGpuList = debounce(this.fetchGpuList, 300);
   },
 
@@ -546,6 +561,61 @@ export default {
     },
   },
   methods: {
+    /* outcoms导出 */
+    async outExport() {
+      try {
+        // 添加确认对话框
+        await this.$confirm("确认要导出Outcomes数据吗?", "提示", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        });
+
+        // 准备导出参数
+        const params = {
+          keyword: this.Sota_searchForm.keyword || undefined
+        };
+
+        // 调用导出接口
+        const response = await exportOutcoms(params);
+
+        if (response.status === 200) {
+          const downloadUrl = response.data.data.download_url;
+
+          // 显示下载链接对话框
+          await this.$alert(
+            `<div>
+              <p>导出成功!请点击下方链接下载文件:</p>
+              <p style="word-break: break-all;">
+                <a href="${downloadUrl}" target="_blank">${downloadUrl}</a>
+              </p>
+              <p>您也可以复制链接后在新窗口打开</p>
+            </div>`,
+            "下载链接",
+            {
+              dangerouslyUseHTMLString: true,
+              confirmButtonText: "复制链接",
+              callback: (action) => {
+                if (action === "confirm") {
+                  // 复制链接到剪贴板
+                  const textarea = document.createElement("textarea");
+                  textarea.value = downloadUrl;
+                  document.body.appendChild(textarea);
+                  textarea.select();
+                  document.execCommand("copy");
+                  document.body.removeChild(textarea);
+                  this.$message.success("链接已复制到剪贴板");
+                }
+              },
+            }
+          );
+        }
+      } catch (error) {
+        if (error === "cancel") return;
+        this.$message.error(error.message || "导出失败");
+        console.error("导出错误:", error);
+      }
+    },
     // 添加获取分类列表方法
     async fetchCategories() {
       try {
@@ -558,7 +628,7 @@ export default {
         this.$message.error("获取分类列表失败");
       }
     },
-    // 添加导出处理方法
+    // 修改导出处理方法
     async handleExport() {
       try {
         const ids = this.selectedRows.map((row) => row.id);
@@ -570,36 +640,43 @@ export default {
           type: "warning",
         });
 
-        // 调用导出接口
-        const response = await axios({
-          url: `${process.env.VUE_APP_BASE_API}/literature-data/export/`,
-          method: "POST",
-          data: { ids },
-          responseType: "blob", // 指定响应类型为 blob
-        });
+        // 调用导出接口获取下载链接
+        const response = await axios.post(
+          `${process.env.VUE_APP_BASE_API}/literature-data/export/`,
+          { ids }
+        );
 
-        // 创建下载链接
-        const blob = new Blob([response.data], {
-          type: response.headers["content-type"],
-        });
-        const url = window.URL.createObjectURL(blob);
-        const link = document.createElement("a");
-        link.href = url;
-
-        // 从响应头获取文件名,如果没有则使用默认名称
-        const filename = response.headers["content-disposition"]
-          ? decodeURIComponent(
-              response.headers["content-disposition"].split("filename=")[1]
-            )
-          : "文献导出.xlsx";
-
-        link.setAttribute("download", filename);
-        document.body.appendChild(link);
-        link.click();
-        document.body.removeChild(link);
-        window.URL.revokeObjectURL(url);
-
-        this.$message.success("导出成功");
+        if (response.status === 200) {
+          const downloadUrl = response.data.data.data.download_url;
+
+          // 显示下载链接对话框
+          await this.$alert(
+            `<div>
+              <p>导出成功!请点击下方链接下载文件:</p>
+              <p style="word-break: break-all;">
+                <a href="${downloadUrl}" target="_blank">${downloadUrl}</a>
+              </p>
+              <p>您也可以复制链接后在新窗口打开</p>
+            </div>`,
+            "下载链接",
+            {
+              dangerouslyUseHTMLString: true,
+              confirmButtonText: "复制链接",
+              callback: (action) => {
+                if (action === "confirm") {
+                  // 复制链接到剪贴板
+                  const textarea = document.createElement("textarea");
+                  textarea.value = downloadUrl;
+                  document.body.appendChild(textarea);
+                  textarea.select();
+                  document.execCommand("copy");
+                  document.body.removeChild(textarea);
+                  this.$message.success("链接已复制到剪贴板");
+                }
+              },
+            }
+          );
+        }
       } catch (error) {
         if (error === "cancel") return;
         this.$message.error(error.message || "导出失败");
@@ -1089,7 +1166,7 @@ export default {
       }
     },
 
-    // 修改 fetchGpuList 方法中的分页处理部分
+    // 修改 fetchGpuList 方法
     async fetchGpuList() {
       this.isLoading = true;
       try {
@@ -1100,52 +1177,42 @@ export default {
           tag_names: this.searchForm.id,
           create_time_start: this.searchForm.createTimeRange?.[0] || "",
           create_time_end: this.searchForm.createTimeRange?.[1] || "",
-          update_time_start: this.searchForm.updateTimeRange?.[0] || "", // 新增
-          update_time_end: this.searchForm.updateTimeRange?.[1] || "", // 新增
+          update_time_start: this.searchForm.updateTimeRange?.[0] || "",
+          update_time_end: this.searchForm.updateTimeRange?.[1] || "",
           processing_status: this.searchForm.processing_status || null,
         };
 
         const response = await axios.post(
           `${process.env.VUE_APP_BASE_API}/literature-data/search/`,
-          params,
-          {
-            headers: {
-              "Content-Type": "application/json",
-            },
-          }
+          params
         );
 
         if (response.status !== 200) return;
 
         // 更新数据和分页信息
         const { list, total, current_page, page_size } = response.data.data;
+
+        // 处理列表数据
         this.gpuList = list.map((item) => {
           const nameParts = item.name.split(".");
-          const processedItem = {
+          return {
             ...item,
             name: nameParts[0],
             type: nameParts.length > 1 ? nameParts.pop() : "未知",
+            processing_status: Number(item.processing_status),
           };
+        });
 
-          // 检查处理状态,如果是处理中且没有设置轮询,则开始轮询
-          if (
-            processedItem.processing_status === 1 &&
-            !this.pollingTimers[processedItem.id]
-          ) {
-            this.startPolling(processedItem.id);
-          }
-          // 如果状态不是处理中,但存在轮询,则停止轮询
-          else if (
-            processedItem.processing_status !== 1 &&
-            this.pollingTimers[processedItem.id]
-          ) {
-            this.stopPolling(processedItem.id);
-          }
+        // 获取需要查询详细状态的文献ID(处理中和处理失败的)
+        const needStatusCheckIds = this.gpuList
+          .filter((item) => [1, 3, 5].includes(Number(item.processing_status)))
+          .map((item) => item.id);
 
-          return processedItem;
-        });
+        if (needStatusCheckIds.length > 0) {
+          await this.fetchBatchStatus(needStatusCheckIds);
+        }
 
-        // 确保这些值都是数字类型
+        // 更新分页信息
         this.total = parseInt(total);
         this.currentPage = parseInt(current_page);
         this.pageSize = parseInt(page_size);
@@ -1156,6 +1223,46 @@ export default {
         this.isLoading = false;
       }
     },
+
+    // 修改批量获取状态的方法
+    async fetchBatchStatus(ids) {
+      try {
+        const statusPromises = ids.map((id) => literResult(id));
+        const responses = await Promise.allSettled(statusPromises);
+
+        responses.forEach((response, index) => {
+          if (
+            response.status === "fulfilled" &&
+            response.value.status === 200
+          ) {
+            const id = ids[index];
+            const item = this.gpuList.find((item) => item.id === id);
+            if (item) {
+              const literatureInfo = response.value.data.data.literature_info;
+              const status = Number(literatureInfo.processing_status);
+
+              // 只更新处理中和处理失败的状态信息
+              if ([1, 3, 5].includes(status)) {
+                this.$set(item, "processing_status", status);
+                this.$set(
+                  item,
+                  "progress_msg",
+                  literatureInfo.status_description
+                );
+
+                // 如果状态是处理中,启动轮询
+                if (status === 1 || status === 5) {
+                  this.startPolling(id);
+                }
+              }
+            }
+          }
+        });
+      } catch (error) {
+        console.error("批量获取状态失败:", error);
+      }
+    },
+
     // 新增:开始轮询
     startPolling(id) {
       if (this.pollingTimers[id]) return;
@@ -1166,20 +1273,13 @@ export default {
           if (response.status === 200) {
             const item = this.gpuList.find((item) => item.id === id);
             if (item) {
-              // 使用接口返回的状态数据
-              const newStatus = Number(
-                response.data.data.literature_info.processing_status
-              );
-
-            /*进度   const newProgress = Number(
-                response.data.data.progress_info.calculated_progress.percentage
-              ); */
-              const newProgressMsg =
-                response.data.data.literature_info.status_description;
+              // 获取状态信息
+              const literatureInfo = response.data.data.literature_info;
+              const newStatus = Number(literatureInfo.processing_status);
+              const newProgressMsg = literatureInfo.status_description;
 
               // 更新状态相关数据
               this.$set(item, "processing_status", newStatus);
-           /*    this.$set(item, "progress", newProgress); */
               this.$set(item, "progress_msg", newProgressMsg);
 
               // 根据状态处理结果
@@ -1188,9 +1288,12 @@ export default {
                 this.$message.success("处理成功");
                 this.stopPolling(id);
                 await this.fetchGpuList();
-              } else if (newStatus === 4) {
-                // 处理失败
-                this.$message.error(newProgressMsg || "处理失败");
+              } else if (newStatus === 3) {
+                // 处理失败 - 保存失败原因
+                const failureReason =
+                  literatureInfo.status_description || "处理失败";
+                this.$set(item, "progress_msg", failureReason);
+                this.$message.error(failureReason);
                 this.stopPolling(id);
               } else if (newStatus === 1 || newStatus === 5) {
                 // 继续轮询
@@ -1252,6 +1355,7 @@ export default {
             name: row.name,
             tag: JSON.stringify(row.tags),
             identifier: row.identifier,
+            type: row.type,
           },
         });
         // 这里可以添加详情处理逻辑
@@ -1276,14 +1380,41 @@ export default {
     async loadSotaDetails() {
       try {
         // 这里调用API获取SOTA详情数据
-        const response = await getSotaDetails();
-        this.sotaDetailsList = response.data;
+        const response = await getSotaDetails(this.Sota_searchForm);
+        this.sotaDetailsList = response.data.list;
+        this.Sota_searchForm.page_size = response.data.page_size;
+        this.Sota_searchForm.page = response.data.current_page;
+        this.Stoa_total = response.data.total;
       } catch (error) {
         /* this.$message.error("获取SOTA详情失败");
         console.error(error); */
       }
     },
 
+    /* 详情翻页 */
+    async handleStoaSizeChange(val) {
+      this.Sota_searchForm.page_size = val;
+      this.Sota_searchForm.page = 1;
+      await this.loadSotaDetails();
+    },
+
+    /* 详情 */
+    async handleStoaCurrentChange(val) {
+      this.Sota_searchForm.page = val;
+      await this.loadSotaDetails();
+    },
+    /* 详情搜索 */
+    handleSotaSearch() {
+      this.loadSotaDetails();
+    },
+    /* 详情重置 */
+    resetSotaSearch() {
+      this.Sota_searchForm = {
+        keyword: "",
+      };
+      this.loadSotaDetails();
+    },
+
     // 加载SOTA汇总表数据
     async loadSotaSummary() {
       try {

+ 3 - 0
src/views/project/ProjectInput.vue

@@ -1717,4 +1717,7 @@ export default {
     font-size: 12px; // 设置字体大小
   }
 }
+::v-deep .el-dialog {
+  margin-top: 0 !important;
+}
 </style>

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 618 - 120
src/views/project/components/dataList.vue


Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio