yangg 7 kuukautta sitten
vanhempi
sitoutus
a5d4161a12
37 muutettua tiedostoa jossa 467 lisäystä ja 84 poistoa
  1. 2 2
      .env.development
  2. 0 0
      dist/index.html
  3. 0 1
      dist/static/css/chunk-26dfb7ab.800aaa04.css
  4. 0 0
      dist/static/css/chunk-2fed8cf3.b5743392.css
  5. 0 0
      dist/static/css/chunk-35a1fd98.ecd5d358.css
  6. 0 0
      dist/static/css/chunk-534f5a65.1b96611e.css
  7. 0 0
      dist/static/css/chunk-5bc2c8fb.dd9f2d1a.css
  8. 0 0
      dist/static/css/chunk-a239f95e.c30800b1.css
  9. 1 0
      dist/static/css/chunk-dbd45e38.46a88685.css
  10. 0 0
      dist/static/css/chunk-ec55d346.1ea26aa2.css
  11. 0 0
      dist/static/js/app.45364835.js
  12. 0 0
      dist/static/js/chunk-079de15b.78b7f793.js
  13. 0 0
      dist/static/js/chunk-0a958739.0b229f25.js
  14. 0 0
      dist/static/js/chunk-26dfb7ab.87cd8a04.js
  15. 0 0
      dist/static/js/chunk-2fed8cf3.ecc13420.js
  16. 0 0
      dist/static/js/chunk-35a1fd98.d4cfc313.js
  17. 0 0
      dist/static/js/chunk-48f27aa5.d3517214.js
  18. 0 0
      dist/static/js/chunk-534f5a65.6cf360c8.js
  19. 0 0
      dist/static/js/chunk-5bc2c8fb.31ae27c6.js
  20. 0 0
      dist/static/js/chunk-6f0044a0.a24c7ca4.js
  21. 0 0
      dist/static/js/chunk-708604c0.956d6f9c.js
  22. 0 0
      dist/static/js/chunk-a239f95e.dd37555f.js
  23. 0 0
      dist/static/js/chunk-b10560d0.a98e9c26.js
  24. 0 0
      dist/static/js/chunk-c0d178b4.7cdbe3c6.js
  25. 0 0
      dist/static/js/chunk-cda0aa98.3200e496.js
  26. 0 0
      dist/static/js/chunk-dbd45e38.b383ae3a.js
  27. 0 0
      dist/static/js/chunk-e08ed888.226779af.js
  28. 0 0
      dist/static/js/chunk-ec55d346.3d3de6f9.js
  29. 0 0
      dist/static/js/chunk-f0e12ffc.a2e53f2c.js
  30. 7 1
      src/api/knowledge.js
  31. 3 1
      src/views/dashboard/admin/chart.vue
  32. 70 22
      src/views/knowledgeMenu/category/components/dataSearch.vue
  33. 9 9
      src/views/knowledgeMenu/category/knowledgeSet.vue
  34. 262 43
      src/views/login/aiIndex.vue
  35. 3 2
      src/views/middPage/list.vue
  36. 11 0
      src/views/project/components/dataInfo.vue
  37. 99 3
      src/views/project/components/dataList.vue

+ 2 - 2
.env.development

@@ -2,7 +2,7 @@
 ENV = 'development'
 port = 8080
 # base api
-VUE_APP_BASE_API = 'http://192.168.66.187:8084'
+VUE_APP_BASE_API = 'http://121.36.251.245:8084'
 #192.168.66.187
-
+#测试服务器:121.36.251.245:8084
 #183.195.216.54

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/index.html


+ 0 - 1
dist/static/css/chunk-26dfb7ab.800aaa04.css

@@ -1 +0,0 @@
-.gpu-list[data-v-39766594]{padding:20px}.filter-section[data-v-39766594]{margin-bottom:20px}.ml-15[data-v-39766594]{margin-left:15px}.pagination[data-v-39766594]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;margin-top:15px}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/css/chunk-2fed8cf3.b5743392.css


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/css/chunk-35a1fd98.ecd5d358.css


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/css/chunk-534f5a65.1b96611e.css


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/css/chunk-5bc2c8fb.dd9f2d1a.css


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/css/chunk-a239f95e.c30800b1.css


+ 1 - 0
dist/static/css/chunk-dbd45e38.46a88685.css

@@ -0,0 +1 @@
+.gpu-list[data-v-61823c94]{padding:20px}.filter-section[data-v-61823c94]{margin-bottom:20px}.ml-15[data-v-61823c94]{margin-left:15px}.pagination[data-v-61823c94]{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end;margin-top:15px}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/css/chunk-ec55d346.1ea26aa2.css


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/app.45364835.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-079de15b.78b7f793.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-0a958739.0b229f25.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-26dfb7ab.87cd8a04.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-2fed8cf3.ecc13420.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-35a1fd98.d4cfc313.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-48f27aa5.d3517214.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-534f5a65.6cf360c8.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-5bc2c8fb.31ae27c6.js


+ 0 - 0
dist/static/js/chunk-6f0044a0.1b4f793d.js → dist/static/js/chunk-6f0044a0.a24c7ca4.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-708604c0.956d6f9c.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-a239f95e.dd37555f.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-b10560d0.a98e9c26.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-c0d178b4.7cdbe3c6.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-cda0aa98.3200e496.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-dbd45e38.b383ae3a.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-e08ed888.226779af.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-ec55d346.3d3de6f9.js


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
dist/static/js/chunk-f0e12ffc.a2e53f2c.js


+ 7 - 1
src/api/knowledge.js

@@ -354,7 +354,13 @@ export function configDelete(data) {
       data
     })
   } 
-
+  /* 单个处理任务监听 */
+  export function resultTask(data) {
+    return request({
+      url: `tasks/result/?document_id=${data}`,
+      method: 'get',
+    })
+  } 
   /* 获取解析结果  */
   
   export function getDetail(data) {

+ 3 - 1
src/views/dashboard/admin/chart.vue

@@ -123,7 +123,7 @@
                                   <h6 class="mb-0 mt-3">
                                     空闲内存:
                                     <span class="text-primary">{{
-                                      currentPercentage.free + "GB"
+                                      currentPercentage.available + "GB"
                                     }}</span>
                                   </h6>
                                   <h6>
@@ -568,6 +568,7 @@ export default {
         try {
           const data = JSON.parse(event.data);
           // 检查数据结构是否完整
+          console.log("wsData",data);
           if (
             data &&
             data.data &&
@@ -575,6 +576,7 @@ export default {
             data.data.system.cpu &&
             data.data.system.cpu.memory
           ) {
+            
             // 更新图表数据
             this.chartData = data;
             this.cpuValue = data.data.system.cpu.total_usage;

+ 70 - 22
src/views/knowledgeMenu/category/components/dataSearch.vue

@@ -25,15 +25,21 @@
         </el-select>
       </el-form-item>
       <el-form-item label="文件类型:">
-          <el-select v-model="queryForm.type" class="m-2" clearable placeholder="请选择文件类型" size="large">
-              <el-option
-                v-for="item in typeList"
-                :key="item"
-                :label="item"
-                :value="item"
-              />
-            </el-select>
-        </el-form-item>
+        <el-select
+          v-model="queryForm.type"
+          class="m-2"
+          clearable
+          placeholder="请选择文件类型"
+          size="large"
+        >
+          <el-option
+            v-for="item in typeList"
+            :key="item"
+            :label="item"
+            :value="item"
+          />
+        </el-select>
+      </el-form-item>
 
       <el-form-item>
         <el-button type="primary" @click="onSubmit">
@@ -85,6 +91,24 @@
       :before-close="handleUploadClose"
     >
       <el-form :model="uploadForm" ref="uploadRef" :rules="uploadRules">
+        <!-- 添加目录选择 -->
+        <el-form-item label="选择目录:" prop="doc_type_id">
+          <el-select
+            v-model="uploadForm.doc_type_id"
+            placeholder="请选择目录"
+            style="width: 100%"
+            clearable
+            filterable
+          >
+            <el-option
+              v-for="item in folderList"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+            <el-option label="其他" value="0"></el-option>
+          </el-select>
+        </el-form-item>
         <el-form-item>
           <el-upload
             class="upload-demo custom-upload"
@@ -93,13 +117,14 @@
             :data="{
               bucket_id: queryForm.bucket_id,
               user_id: queryForm.user_id,
-              doc_type_id:0
+              doc_type_id: uploadForm.doc_type_id, // 修改这里,使用选择的目录ID
             }"
             multiple
             :on-change="handleFileChange"
             :file-list="fileList"
             :on-success="handleAvatarSuccess"
             :on-error="handleUploadError"
+            :disabled="!uploadForm.doc_type_id"
           >
             <i class="el-icon-upload"></i>
             <div class="el-upload__text">
@@ -120,7 +145,12 @@
 </template>
 
 <script>
-import { getBucketContents, postUpload,getSuffixName } from "@/api/knowledge";
+import {
+  getBucketContents,
+  postUpload,
+  getSuffixName,
+  selectTypeList,
+} from "@/api/knowledge";
 export default {
   props: {
     queryForm: {
@@ -202,8 +232,8 @@ export default {
       uploadForm: {},
       fileList: [],
       url: "",
-      typeList:[
-       /*  {
+      typeList: [
+        /*  {
           label:'xls',
           value:'xls',
         },
@@ -223,14 +253,28 @@ export default {
           label:'png',
           value:'png',
         }, */
-      ]
+      ],
+      typeForm: {
+        page: 1,
+        pageSize: 9999,
+        kb_id: this.$route.query.id,
+      },
+      uploadForm: {
+        doc_type_id: "", // 添加目录ID字段
+      },
+      uploadRules: {
+        doc_type_id: [
+          { required: true, message: "请选择目录", trigger: "change" },
+        ],
+      },
+      folderList: [], // 确保这个字段存在
     };
   },
   mounted() {
     this.url = process.env.VUE_APP_BASE_API;
     this.queryForm.bucket_id = this.$route.query.id;
     this.queryForm.user_id = this.$store.state.user.id;
-    this.getType()
+    this.getType();
   },
   methods: {
     onCreate(e) {
@@ -238,7 +282,6 @@ export default {
     },
     //搜索
     onSubmit() {
-     
       this.$emit("bindSetQuery", this.$props.queryForm);
     },
     /* 创建模版 */
@@ -291,8 +334,9 @@ export default {
     },
     handleAvatarSuccess(response, res, file) {
       if (response.status == 200) {
-        
         this.$emit("bindSetQuery", this.$props.queryForm);
+        // 添加新的事件发送,通知父组件刷新目录列表
+        this.$emit("refreshFolders");
         this.$message.success("添加成功!");
         this.uploadVisible = false;
       } else {
@@ -313,11 +357,15 @@ export default {
       this.uploadVisible = false;
       this.fileList = [];
     },
-    getType(){
-      getSuffixName().then(res=>{
-       this.typeList=res.data
-      })
-    }
+    getType() {
+      getSuffixName().then((res) => {
+        this.typeList = res.data;
+      });
+      selectTypeList(this.typeForm).then((res) => {
+        if (res.status !== 200) return;
+        this.folderList = res.data.dataList;
+      });
+    },
     /* submitUpload() {
       this.fileList.map((el) => {
         postUpload({ bucket_name: this.queryForm.bucket_name, file: el }).then(

+ 9 - 9
src/views/knowledgeMenu/category/knowledgeSet.vue

@@ -49,7 +49,10 @@
       </el-tree>
     </div>
     <div class="right">
-      <dataSearch @bindSetQuery="setQuery"></dataSearch>
+      <dataSearch
+        @bindSetQuery="setQuery"
+        @refreshFolders="typeList"
+      ></dataSearch>
       <!--  <dataList
         :queryForm="queryForm"
         :allowEdit="checkAuth('/document/update')"
@@ -91,12 +94,7 @@
         >
           <el-table-column type="selection" width="55"></el-table-column>
           <el-table-column prop="id" label="ID" align="center" width="80" />
-          <el-table-column
-            prop="name"
-            label="文件名称"
-            sortable
-            width="350"
-          >
+          <el-table-column prop="name" label="文件名称" sortable width="350">
             <template #default="scope">
               <span>{{ scope.row.name }}</span>
             </template>
@@ -413,6 +411,7 @@ import {
   delDocumentList,
   getdocpro,
   analysisPro,
+  resultTask,
 } from "@/api/knowledge";
 export default {
   components: {
@@ -652,7 +651,7 @@ export default {
           // 用户取消删除操作,不做任何处理
         });
     },
-
+    /* 目录初始化 */
     typeList() {
       this.loading = true;
       Promise.all([
@@ -935,7 +934,8 @@ export default {
       }
 
       const checkStatus = () => {
-        getdocpro({ document_id: row.id })
+        resultTask(row.id)
+          /*   getdocpro({ document_id: row.id }) */
           .then((res) => {
             if (res.status === 200) {
               const newStatus = Number(res.data.status);

+ 262 - 43
src/views/login/aiIndex.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,58 @@
           placeholder="发送消息..."
         ></textarea>
         <button @click="sendMessage" class="btn">发送</button>
+      </div> -->
+      <div class="input-container">
+        <!-- 文件上传预览区 -->
+        <div class="upload-preview" v-if="uploadFiles.length > 0">
+          <div
+            v-for="(file, index) in uploadFiles"
+            :key="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>
+
+        <!-- 输入区域 -->
+        <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="sendMessage" class="btn">发送</button>
+          </div>
+        </div>
       </div>
       <!-- </div> -->
     </div>
@@ -67,7 +119,7 @@ import ChatBox from "@/components/webAi/index.vue";
 import LoginModal from "@/components/LoginModal";
 import MarkdownIt from "markdown-it";
 import { pcInnerAi, getMinioURl } from "@/api/api";
-import { modelList,get_default } from "@/api/knowledge";
+import { modelList, get_default } from "@/api/knowledge";
 import axios from "axios";
 const md = new MarkdownIt();
 
@@ -111,12 +163,12 @@ export default {
         documentDirectory: "",
         document: "",
       },
-      currentChat_id:'',
-      phone:''
+      currentChat_id: "",
+      phone: "",
+      uploadFiles: [], // 存储上传的文件
     };
   },
   mounted() {
-    localStorage.removeItem("AIData")
     this.checkAIData();
     if (this.$route.name == "ai") {
       /* 外部知识库 */
@@ -131,7 +183,7 @@ export default {
         if (AIData && AIData.data) {
           console.log(AIData);
           this.wsUrl = AIData.data.url || "";
-          this.phone=AIData.data.phone || ""
+          this.phone = AIData.data.phone || "";
           this.tweaks = AIData.data.tweaks || {};
         } else {
           console.error("Invalid AIData structure");
@@ -149,9 +201,53 @@ export default {
         this.tweaks = res.data.tweaks;
       });
     }
-    this.init()
+    this.init();
   },
   methods: {
+    /* 上传文件 */
+    async handleFileUpload(event) {
+      const files = Array.from(event.target.files);
+
+      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`,
+            formData,
+            {
+              headers: {
+                "Content-Type": "multipart/form-data",
+              },
+            }
+          );
+
+          // 获取接口返回的文件URL
+          const fileUrl = response.data.data.fileUrl; // 根据实际接口返回格式调整
+          // 将文件信息添加到 uploadFiles 数组
+          this.uploadFiles.push({
+            name: file.name,
+            type: file.type,
+            preview: fileUrl, // 直接使用接口返回的URL作为预览
+            url: fileUrl,
+          });
+        } catch (error) {
+          console.error("文件上传失败:", error);
+          this.$message.error(`文件 ${file.name} 上传失败`);
+        }
+      }
+
+      // 清空input以允许重复上传相同文件
+      event.target.value = "";
+    },
+
+    removeFile(index) {
+      this.uploadFiles.splice(index, 1);
+    },
+    /*  */
     checkAIData() {
       const aiData = localStorage.getItem("AIData");
       this.showLoginModal = !aiData;
@@ -284,10 +380,17 @@ export default {
         }
       }
       const chatId = this.getUuid();
-      const wsUrl = `${process.env.VUE_APP_BASE_API}/chatbot/chat` //this.wsUrl;'http://58.246.234.210:7860/api/v1/run/3ef7369e-b617-40d2-9e2e-54f3ee76ed2e?stream=false'
+      const wsUrl = `${process.env.VUE_APP_BASE_API}/chatbot/multimodal`; ///chatbot/chatthis.wsUrl;'http://58.246.234.210:7860/api/v1/run/3ef7369e-b617-40d2-9e2e-54f3ee76ed2e?stream=false'
       let message = this.userInput.trim();
-      if (message) {
-        // Markdown换行:在每个换行符之前添加两个空格
+      // 获取所有上传文件的URL并组合成字符串
+      // 修改:直接将图片URL组装成数组
+      const imageUrls = this.uploadFiles
+        .filter((file) => file.type.startsWith("image/"))
+        .map((file) => file.url); // 直接获取URL数组,不需要join
+
+      if (message || imageUrls.length>0) {
+        // 修改判断条件,允许只有图片没有文字的情况
+        // Markdown换行处理
         message = message.replace(/(\r\n|\r|\n)/g, "  \n");
 
         this.messages.push({
@@ -297,8 +400,12 @@ export default {
           user: "user",
           done: true,
         });
+
         this.userInput = "";
-        // 添加机器人的响应消息(初始为空)
+        // 清空上传的文件
+        this.uploadFiles = [];
+
+        // 添加机器人的响应消息
         const botMessage = {
           user: "bot",
           messageType: "TEXT",
@@ -309,8 +416,13 @@ export default {
         };
         this.messages.push(botMessage);
 
-        // 开始模拟思考
-        const thinkingPromise = this.simulateThinking(botMessage);
+        // 开始思考动画
+        const thinkingController = new AbortController();
+        const thinkingPromise = this.simulateThinking(
+          botMessage,
+          thinkingController.signal
+        );
+
         // 通过 HTTP 发送消息
         try {
           ///send-message
@@ -324,9 +436,11 @@ export default {
               message: message,
               /* output_type: "chat",
               input_type: "chat", */
-              chat_config_id:'3',//this.currentChat_id,
-              user_id: this.phone,//this.tweaks.Memory-KtRT7.session_id,
-              session_id: this.session_id || ""/* {
+              chat_config_id: "17", //this.currentChat_id,
+              user_id: this.phone, //this.tweaks.Memory-KtRT7.session_id,
+              session_id: this.session_id || "",
+              image_url: imageUrls,
+              history_limit: 10 /* {
                 "ChatInput-U82Vu": {},
                 "Prompt-b8Z1E": {},
                 "OllamaModel-z9SVx": {},
@@ -340,10 +454,8 @@ export default {
               } */,
             }),
           });
-
           const result = await response.json();
           this.session_id = result.data.session_id;
-          console.log(result);
           if (result.code !== 200) {
             const errorText = await response.text(); // 获取错误文本
             throw new Error(
@@ -351,14 +463,16 @@ export default {
             );
           }
           // 等待思考动画完成
+          // 停止思考动画
+          thinkingController.abort();
           await thinkingPromise;
           if (this.$route.name !== "ai") {
             // 提取 additional_input 数据
             const additionalInput = result.data.milvus_ids;
 
             // 处理字符串,提取 ID 值
-            this.idArray = additionalInput
-             /*  .split("\n") // 按换行符分割
+            this.idArray = additionalInput;
+            /*  .split("\n") // 按换行符分割
               .map((line) => line.trim()) // 去除每行首尾空白
               .filter((line) => line.startsWith("ID:")) // 只保留以 'ID:' 开头的行
               .map((line) => line.substring(3).trim()); // 提取 'ID:' 后面的内容并去除空白 */
@@ -375,7 +489,9 @@ export default {
           this.handleResponse(result, botMessage);
         } catch (error) {
           console.error("Error sending message:", error);
-          // 如果发生错误,停止思考动画
+          // 停止思考动画
+          thinkingController.abort();
+          await thinkingPromise;
           botMessage.done = true;
           botMessage.message = "抱歉,发生了错误,请稍后再试。";
         }
@@ -416,36 +532,46 @@ export default {
         }
       }
     },
-    async simulateThinking(message) {
+    async simulateThinking(message, signal) {
       this.isThinking = true;
-      const thinkingTime = Math.random() * 1000 + 500; // 思考时间为 0.5-1.5 秒
       const dotInterval = 200; // 每 200ms 更新一次点
 
-      const updateDots = () => {
-        this.thinkingDots += ".";
-        if (this.thinkingDots.length > 3) {
-          this.thinkingDots = "";
-        }
-        message.message = this.thinkingDots;
-      };
-
-      const intervalId = setInterval(updateDots, dotInterval);
+      return new Promise((resolve) => {
+        const updateDots = () => {
+          if (signal.aborted) {
+            clearInterval(intervalId);
+            this.isThinking = false;
+            this.thinkingDots = "";
+            message.message = "";
+            resolve();
+            return;
+          }
 
-      await new Promise((resolve) => setTimeout(resolve, thinkingTime));
+          this.thinkingDots += ".";
+          if (this.thinkingDots.length > 3) {
+            this.thinkingDots = "";
+          }
+          message.message = "思考中" + this.thinkingDots;
+        };
 
-      clearInterval(intervalId);
-      this.isThinking = false;
-      this.thinkingDots = "";
-      message.message = "";
+        const intervalId = setInterval(updateDots, dotInterval);
+        signal.addEventListener("abort", () => {
+          clearInterval(intervalId);
+          this.isThinking = false;
+          this.thinkingDots = "";
+          message.message = "";
+          resolve();
+        });
+      });
     },
 
     async handleResponse(value, existingMessage) {
-      const data = value.data.answer
+      const data = value.data.answer.answer;
 
       existingMessage.messageType = data;
       /* existingMessage.time = data.timestamp; */
-      existingMessage.message = '';
-      
+      existingMessage.message = "";
+
       let mainText = data;
       let sourceText = "";
 
@@ -738,7 +864,7 @@ export default {
   overflow-y: auto; /* 如果消息太多,允许滚动 */
   height: calc(100vh - 230px); /* 高度需要根据输入框和其他元素的高度调整 */
   padding: 10px; /* 或你希望的内边距大小 */
-  margin-bottom: 120px;
+  margin-bottom: 110px;
 }
 /* 针对手机端的媒体查询 */
 @media screen and (max-width: 768px) {
@@ -875,8 +1001,9 @@ export default {
   max-width: 800px; /* 设置最大宽度 */
   display: flex;
   justify-content: space-between;
-  align-items: center;
-  padding: 10px;
+  align-items: flex-start;
+  flex-direction: column;
+  padding: 15px;
   background: white;
   border-radius: 10px;
   box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.1);
@@ -933,4 +1060,96 @@ textarea {
     margin: 0 auto; /* 居中显示 */
   }
 }
+
+/* 新输入框 */
+.upload-preview {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  margin-bottom: 10px;
+  max-height: 100px;
+  overflow-y: auto;
+}
+
+.preview-item {
+  position: relative;
+  width: 80px;
+  height: 80px;
+  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;
+}
 </style>

+ 3 - 2
src/views/middPage/list.vue

@@ -827,11 +827,12 @@ export default {
           type: "warning",
         });
 
-        const res = await process_model_generation(ids);
+        const res = await process_model_regeneration(ids);//process_model_regeneration
 
         if (res.status === 200) {
           this.$message.success("批量处理已提交");
           this.selectedRows = []; // 清空选中
+          this.clearSelection()
           this.allSelectedRows.clear();
           await this.fetchGpuList(); // 刷新列表
         } else {
@@ -1107,7 +1108,7 @@ export default {
           console.error("轮询检查状态失败:", error);
           this.stopPolling(id); // 发生错误时停止轮询
         }
-      }, 30000); // 每分钟检查一次
+      }, 10000); // 每分钟检查一次
     },
 
     // 新增:停止轮询

+ 11 - 0
src/views/project/components/dataInfo.vue

@@ -13,6 +13,8 @@
           class="form-item-half"
         >
           <el-select
+            clearable
+            filterable
             v-model="dataForm.customerId"
             placeholder="请选择客户"
             size="large"
@@ -34,6 +36,8 @@
 
         <el-form-item label="产品:" prop="product" class="form-item-half">
           <el-select
+            clearable
+            filterable
             v-model="dataForm.product"
             placeholder="请选择产品"
             size="large"
@@ -52,6 +56,8 @@
           class="form-item-half"
         >
           <el-select
+            clearable
+            filterable
             v-model="dataForm.ProjectCategory"
             placeholder="请选择项目分类"
             size="large"
@@ -91,6 +97,8 @@
           class="form-item-half"
         >
           <el-select
+            clearable
+            filterable
             v-model="dataForm.current_phase"
             placeholder="请选择当前阶段"
             size="large"
@@ -110,6 +118,8 @@
           class="form-item-half"
         >
           <el-select
+            clearable
+            filterable
             v-model="dataForm.project_status"
             placeholder="请选择目前状态"
             size="large"
@@ -128,6 +138,7 @@
           class="form-item-half"
         >
           <el-date-picker
+            clearable
             v-model="dataForm.start_date"
             type="date"
             placeholder="请选择日期"

+ 99 - 3
src/views/project/components/dataList.vue

@@ -189,8 +189,8 @@
         <el-table-column type="selection" width="55"></el-table-column>
         <el-table-column prop="id" label="ID" width="80" align="center" />
         <el-table-column prop="title" label="模版名称" align="left" />
-        <el-table-column prop="create_time" label="绑定时间" align="center" />
-        <el-table-column label="操作" align="center" width="200">
+        <el-table-column prop="createTime" label="绑定时间" align="center" />
+        <el-table-column label="操作" align="center">
           <template #default="scope">
             <el-button type="primary" @click="previewTemplate(scope.row)"
               >预览</el-button
@@ -198,6 +198,9 @@
             <el-button type="primary" @click="replaceTemplate(scope.row)"
               >替换</el-button
             >
+            <el-button type="success" @click="exportDocument(scope.row)"
+              >导出文档</el-button
+            >
           </template>
         </el-table-column>
       </el-table>
@@ -252,7 +255,7 @@ import {
   calculate,
   getphases,
 } from "@/api/project";
-import { searchList, updateDocument } from "@/api/document";
+import { searchList, updateDocument, exportDocument } from "@/api/document";
 import { searchCustomer } from "@/api/customer";
 import { searchProduct } from "@/api/product";
 /* import {searchTemplate} from "@/api/template" */
@@ -333,6 +336,99 @@ export default {
     this.init();
   },
   methods: {
+    // 修改后的导出文档方法
+    async exportDocument(template) {
+      try {
+        // 解析模版内容
+        const templateData = JSON.parse(template.data || "[]");
+
+        // 创建一个临时的容器来存放内容
+        const contentContainer = document.createElement("div");
+        contentContainer.className = "content";
+
+        // 处理每个模版段落
+        templateData.forEach((item) => {
+          const templateSection = document.createElement("div");
+          templateSection.className = "template-textarea";
+          templateSection.innerHTML = item.content;
+          contentContainer.appendChild(templateSection);
+        });
+
+        // 处理所有输入框
+        contentContainer
+          .querySelectorAll('input[type="text"]')
+          .forEach((input) => {
+            const span = document.createElement("span");
+            span.textContent = input.value;
+            input.parentNode.replaceChild(span, input);
+          });
+
+        // 构建HTML内容
+        let contentData = `<!DOCTYPE html>
+        <html>
+          <head>
+            <meta charset="UTF-8">
+            <style>
+              @page {
+                size: A4;
+                margin: 2cm;
+              }
+              body {
+                font-family: Arial, sans-serif;
+                font-size: 12pt;
+                line-height: 1.5;
+              }
+              table {
+                width: 100%;
+                border-collapse: collapse;
+              }
+              td, th {
+                border: 1px solid black;
+                padding: 5px;
+              }
+              .template-textarea {
+                page-break-after: always;
+              }
+              .TOC {
+                display: none !important;
+              }
+            </style>
+          </head>
+          <body>
+            ${contentContainer.innerHTML}
+          </body>
+        </html>`;
+
+        // 处理表格样式
+        contentData = contentData
+          .replaceAll("<table ", `<table style="border-collapse: collapse;" `)
+          .replaceAll("<td>", `<td style="border: 1px solid black;">`);
+
+        // 调用导出接口
+        const res = await exportDocument({
+          content: contentData,
+          title: template.title,
+        });
+
+        if (res.status !== 200) {
+          this.$alert(res.errMsg);
+          return;
+        }
+
+        // 处理文件下载
+        const link = document.createElement("a");
+        link.href = res.data.file_path;
+        link.download = res.data.file_name;
+        document.body.appendChild(link);
+        link.click();
+        document.body.removeChild(link);
+
+        this.$message.success("导出成功");
+      } catch (error) {
+        console.error("导出文档失败:", error);
+        this.$alert("导出文档时发生错误,请稍后重试。");
+      }
+    },
     /* 客户名称 */
     getCusomName(phaseId) {
       const product = this.customList.find((item) => item.id === phaseId);

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä