lsc hai 5 meses
pai
achega
7dc77f66fb

+ 184 - 2
src/components/pages/knowledge/components/checkDialog.vue

@@ -39,7 +39,9 @@
       </div>
       <div class="nav">
         <span :class="{ active: type == 1 }" @click="type = 1">切片</span>
-        <span :class="{ active: type == 2 }" @click="type = 2">图谱</span>
+        <!-- <span :class="{ active: type == 2 }" @click="type = 2">图谱</span> -->
+        <!-- <span :class="{ active: type == 3 }" @click="type = 3">实体</span>
+        <span :class="{ active: type == 4 }" @click="type = 4">关系</span> -->
       </div>
       <div class="chunkBox" v-show="type == 1" v-loading="chunksLoading">
         <div class="chunks" v-for="(item, index) in chunksArray" :key="index">
@@ -57,8 +59,51 @@
         >
         </el-pagination>
       </div>
+      <div class="chunkBox" v-show="type == 3" v-loading="entitiesLoading">
+        <div class="chunks" v-for="(item, index) in entitiesArray" :key="index">
+          <div class="chunksBox">
+            <div><span class="title">实体名:</span><span>{{ item.name }}</span></div>
+            <div><span class="title">描述:</span><span>{{ item.description }}</span></div>
+          </div>
+        </div>
+        <div class="noneData" v-if="!entitiesArray.length">暂无获取实体数据</div>
+        <el-pagination
+          v-if="entitiesArray.length > 0"
+          style="margin-top: 10px"
+          background
+          layout="prev, pager, next"
+          :page-size="limit2"
+          :total="total2"
+          @current-change="handleCurrentChange2"
+        >
+        </el-pagination>
+      </div>
+      <div class="chunkBox" v-show="type == 4" v-loading="relationshipsLoading">
+        <div
+          class="chunks"
+          v-for="(item, index) in relationshipsArray"
+          :key="index"
+        >
+          <div class="chunksBox">
+            <div><span class="title">关系:</span><span>{{ item.subject }} {{ item.predicate }} {{ item.object }}</span></div>
+            <div><span class="title">描述:</span><span>{{ item.description }}</span></div>
+          </div>          
+        </div>
+        <div class="noneData" v-if="!relationshipsArray.length">暂无获取关系数据</div>
+        <el-pagination
+          v-if="relationshipsArray.length > 0"
+          style="margin-top: 10px"
+          background
+          layout="prev, pager, next"
+          :page-size="limit3"
+          :total="total3"
+          @current-change="handleCurrentChange3"
+        >
+        </el-pagination>
+      </div>
       <div class="tuBox" v-show="type == 2">
         <div class="noneData">暂无获取图谱数据</div>
+        <graph :Json="graphJson"></graph>
       </div>
     </div>
     <span slot="footer">
@@ -68,7 +113,11 @@
 </template>
 
 <script>
+import graph from "./graph.vue";
 export default {
+  components: {
+    graph
+  },
   data() {
     return {
       dialogVisible: false,
@@ -80,7 +129,18 @@ export default {
       total: 0,
       page: 1,
       chunksArray: [],
-      chunksLoading: false
+      chunksLoading: false,
+      limit2: 10,
+      total2: 0,
+      page2: 1,
+      entitiesArray: [],
+      entitiesLoading: false,
+      limit3: 10,
+      total3: 0,
+      page3: 1,
+      relationshipsArray: [],
+      relationshipsLoading: false,
+      graphJson: {}
     };
   },
   computed: {
@@ -130,9 +190,15 @@ export default {
     openG(did) {
       this.did = did;
       this.dialogVisible = true;
+      this.type = 1;
       this.page = 1;
+      this.page2 = 1;
+      this.page3 = 1;
       this.getFileDetail();
       this.getChunks();
+      this.setGrapJson();
+      this.getEntities();
+      this.getRelationships();
     },
     getFileDetail() {
       this.isLoading = true;
@@ -174,6 +240,112 @@ export default {
     handleCurrentChange(val) {
       this.page = val;
       this.getChunks();
+    },
+    getEntities() {
+      this.entitiesLoading = true;
+      let params = {
+        documentId: this.did,
+        page: this.page2
+      };
+      // 获取切片
+      this.ajax
+        .post(this.$store.state.fileApi + "getEntities", [params])
+        .then(res => {
+          this.entitiesLoading = false;
+          console.log(res.data);
+          this.total2 = res.data.result.totalEntries;
+          this.entitiesArray = res.data.result.results;
+        })
+        .catch(err => {
+          this.entitiesLoading = false;
+          console.error(err);
+        });
+    },
+    handleCurrentChange2(val) {
+      this.page2 = val;
+      this.getEntities();
+    },
+    getRelationships() {
+      this.relationshipsLoading = true;
+      let params = {
+        documentId: this.did,
+        page: this.page3
+      };
+      // 获取切片
+      this.ajax
+        .post(this.$store.state.fileApi + "getRelationships", [params])
+        .then(res => {
+          this.relationshipsLoading = false;
+          console.log(res.data);
+          this.total3 = res.data.result.totalEntries;
+          this.relationshipsArray = res.data.result.results;
+        })
+        .catch(err => {
+          this.relationshipsLoading = false;
+          console.error(err);
+        });
+    },
+    handleCurrentChange3(val) {
+      this.page3 = val;
+      this.getRelationships();
+    },
+    generateDynamicJson(relationships) {
+      // 用于存储节点和连线的对象
+      let nodes = [];
+      let lines = [];
+      let nodeIds = new Set(); // 用来记录已生成的节点ID,避免重复
+
+      // 遍历关系数据并动态生成节点和连线
+      relationships.forEach(relation => {
+        // 添加subject和object为节点
+        if (!nodeIds.has(relation.subject)) {
+          nodes.push({
+            id: relation.subject,
+            text: relation.subject,
+            borderColor: "yellow"
+          });
+          nodeIds.add(relation.subject);
+        }
+
+        if (!nodeIds.has(relation.object)) {
+          nodes.push({
+            id: relation.object,
+            text: relation.object,
+            borderColor: "yellow"
+          });
+          nodeIds.add(relation.object);
+        }
+
+        // 创建连线
+        lines.push({ from: relation.subject, to: relation.object });
+      });
+
+      // 返回最终的结构
+      return {
+        rootId: "a",
+        nodes,
+        lines
+      };
+    },
+    setGrapJson() {
+      this.graphJson = {
+        rootId: "a",
+        nodes: [
+          { id: "a", text: "Border color", borderColor: "yellow" },
+          { id: "a1", text: "No border", borderColor: "yellow" },
+          { id: "a2", text: "Plain", borderColor: "yellow" },
+          { id: "a1-1", borderColor: "yellow" },
+          { id: "b", text: "b", borderColor: "yellow" },
+          { id: "c", text: "c", borderColor: "yellow" }
+        ],
+        lines: [
+          { from: "a", to: "b" },
+          { from: "a", to: "c" },
+          { from: "a", to: "a1" },
+          { from: "a1", to: "a1-1" },
+          { from: "a", to: "a2" }
+        ]
+      };
     }
   }
 };
@@ -276,4 +448,14 @@ export default {
 .chunks + .chunks {
   margin-top: 10px;
 }
+
+.chunksBox{
+
+}
+
+.chunksBox > div{}
+.chunksBox > div > span{}
+.chunksBox > div > span.title{
+  font-weight: 600;
+}
 </style>

+ 24 - 19
src/components/pages/knowledge/components/relateFiles.vue

@@ -12,16 +12,21 @@
           <div class="f_box_top_title">我的文件</div>
         </div>
         <div class="f_box_top_right">
-          <div class="btn" @click="serchFile">刷新</div>
+          <el-button type="primary" size="small" @click="serchFile"
+            >刷新</el-button
+          >
+          <!-- <div class="btn" @click="serchFile">刷新</div> -->
         </div>
       </div>
       <div class="f_box_top">
         <div class="f_box_top_left">
           <el-select v-model="mofolderid" filterable @change="serchFile">
-            <el-option v-for="item in myFolderArray"
+            <el-option
+              v-for="item in myFolderArray"
               :key="item.folderid"
               :label="item.name"
-              :value="item.folderid">
+              :value="item.folderid"
+            >
             </el-option>
           </el-select>
         </div>
@@ -159,7 +164,7 @@ export default {
       total: 0,
       limit: 10,
       folderLoading: false,
-      myFolderArray: [],
+      myFolderArray: []
     };
   },
   computed: {
@@ -227,16 +232,16 @@ export default {
     getFolder() {
       this.folderLoading = true;
       let params = {
-        uid: this.userid,
+        uid: this.userid
       };
       this.ajax
         .post(this.$store.state.api + "getFolder", [params])
-        .then((res) => {
+        .then(res => {
           this.folderLoading = false;
-          let data = res.data[0]
-          this.myFolderArray = data.filter(el => el.folderid != this.folderid)
+          let data = res.data[0];
+          this.myFolderArray = data.filter(el => el.folderid != this.folderid);
         })
-        .catch((err) => {
+        .catch(err => {
           this.isLoading = false;
           console.error(err);
         });
@@ -285,11 +290,11 @@ export default {
       };
       await this.ajax.post(this.$store.state.fileApi + "moveFile", [params]);
       this.$parent.getData();
-      this.checkArray = []
-      this.checkArray2 = []
+      this.checkArray = [];
+      this.checkArray2 = [];
       this.$refs.myTable.clearSelection();
-      this.dialogVisible = false
-      this.$message.success("添加成功")
+      this.dialogVisible = false;
+      this.$message.success("添加成功");
     }
   }
 };
@@ -306,7 +311,7 @@ export default {
   overflow-y: auto;
   overflow-x: hidden;
   box-sizing: border-box;
-  padding: 20px;
+  padding: 0 20px;
 }
 
 .f_box {
@@ -384,18 +389,18 @@ export default {
 
 .f_box_top_right > .input {
   position: relative;
-  width: 250px;
-  height: 35px;
+  width: 280px;
+  height: 40px;
 }
 
 .f_box_top_right > .input > input {
   width: 100%;
   height: 100%;
-  border: 1px solid #d9d9d9;
+  border: 1px solid #dcdfe6;
   border-radius: 5px;
-  padding: 0 45px 0 10px;
+  padding: 0 45px 0 15px;
   box-sizing: border-box;
-  font-size: 12px;
+  font-size: 14px;
   outline: none;
 }
 

+ 180 - 89
src/components/pages/knowledge/fileBox.vue

@@ -5,30 +5,34 @@
         <div class="f_box_top_title">所有文件</div>
       </div>
       <div class="f_box_top_right">
-        <div class="btn" @click="serchFile">刷新</div>
-        <div class="btn" @click="addImg($event)">
-          上传文件
+        <el-button type="primary" size="small" @click="serchFile"
+          >刷新</el-button
+        >
+        <el-button type="primary" size="small" @click="addImg($event)"
+          >上传文件
           <input
             type="file"
             accept="*"
             style="display: none"
             multiple="multiple"
             @change="beforeUpload($event)"
-          />
-        </div>
-        <div class="btn delBtn" @click="batchDelete">删除</div>
+        /></el-button>
+        <el-button type="danger" size="small" @click="batchDelete"
+          >删除</el-button
+        >
       </div>
     </div>
     <div class="f_box_top">
       <div class="f_box_top_left">
         <el-select v-model="checkFolderid" filterable @change="serchFile">
-          <el-option v-for="item in myFolderArray"
+          <el-option
+            v-for="item in myFolderArray"
             :key="item.folderid"
             :label="item.name"
-            :value="item.folderid">
+            :value="item.folderid"
+          >
           </el-option>
         </el-select>
-        
       </div>
       <div class="f_box_top_right">
         <div class="input">
@@ -55,31 +59,79 @@
           stripe
           style="width: 100%"
           @selection-change="handleSelectionChange"
-          :row-key="(row) => row.id"
+          :row-key="row => row.id"
           ref="myTable"
         >
-          <el-table-column type="selection" width="50px" :reserve-selection="true"></el-table-column>
-          <el-table-column prop="name" label="文件名" show-overflow-tooltip min-width="15"></el-table-column>
-          <el-table-column prop="username" label="拥有者" show-overflow-tooltip min-width="20"></el-table-column>
-          <el-table-column prop="folderName" label="所属文件夹" show-overflow-tooltip min-width="20"></el-table-column>
+          <el-table-column
+            type="selection"
+            width="50px"
+            :reserve-selection="true"
+          ></el-table-column>
+          <el-table-column
+            prop="name"
+            label="文件名"
+            show-overflow-tooltip
+            min-width="15"
+          ></el-table-column>
+          <el-table-column
+            prop="username"
+            label="拥有者"
+            show-overflow-tooltip
+            min-width="20"
+          ></el-table-column>
+          <el-table-column
+            prop="folderName"
+            label="所属文件夹"
+            show-overflow-tooltip
+            min-width="20"
+          ></el-table-column>
           <el-table-column label="处理状态" width="80px">
             <template slot-scope="scope">
               {{ getState(scope.row.ingestionStatus) }}
             </template>
           </el-table-column>
-           <el-table-column label="是否已提取知识图谱" width="150px">
+          <el-table-column label="是否提取" width="80px">
             <template slot-scope="scope">
               {{ getState2(scope.row.extractionStatus) }}
             </template>
           </el-table-column>
-          <el-table-column prop="documentType" label="文件类型" show-overflow-tooltip min-width="10"></el-table-column>
+          <el-table-column
+            prop="documentType"
+            label="文件类型"
+            show-overflow-tooltip
+            min-width="10"
+          ></el-table-column>
           <!-- <el-table-column label="公开状态" show-overflow-tooltip width="80px"></el-table-column> -->
-          <el-table-column prop="time" label="上传时间" show-overflow-tooltip min-width="20"></el-table-column>
-          <el-table-column label="操作" width="200px">
+          <el-table-column
+            prop="time"
+            label="上传时间"
+            show-overflow-tooltip
+            min-width="20"
+          ></el-table-column>
+          <el-table-column label="操作" width="250px">
             <template slot-scope="scope">
               <div>
-                <el-button type="primary" size="small" @click="check(scope.row)" :disabled="scope.row.ingestionStatus == 'failed'">查看</el-button>
-                <el-button type="danger" size="small"  v-if="scope.row.userid == userid"  @click="deleteFile(scope.row.id, scope.row.documentid)">删除</el-button>
+                <el-button
+                  type="primary"
+                  size="small"
+                  @click="extractFile(scope.row)"
+                  :disabled="scope.row.ingestionStatus == 'failed'"
+                  >提取</el-button
+                >
+                <el-button
+                  type="primary"
+                  size="small"
+                  @click="checkFile(scope.row)"
+                  :disabled="scope.row.ingestionStatus == 'failed'"
+                  >查看</el-button
+                >
+                <el-button
+                  type="danger"
+                  size="small"
+                  v-if="scope.row.userid == userid"
+                  @click="deleteFile(scope.row.id, scope.row.documentid)"
+                  >删除</el-button
+                >
               </div>
             </template>
           </el-table-column>
@@ -125,7 +177,7 @@ import wpdf from "../test/file/wPdf2.vue";
 import wOffice from "../test/file/wOffice.vue";
 
 import { v4 as uuidv4 } from "uuid";
-import checkDialog from './components/checkDialog'
+import checkDialog from "./components/checkDialog";
 
 export default {
   components: {
@@ -136,22 +188,22 @@ export default {
   },
   props: {
     userid: {
-      type: String,
+      type: String
     },
     typeArray: {
-      type: Array,
+      type: Array
     },
     pid: {
-      type: String,
+      type: String
     },
     type: {
-      type: String,
+      type: String
     },
     moFolderid: {
       type: String
     },
     myFolderArray: {
-      type: Array,
+      type: Array
     }
   },
   watch: {
@@ -162,10 +214,10 @@ export default {
         this.fileArray = [];
         this.checkArray = [];
         this.fileName = "";
-        this.checkFolderid = this.moFolderid
+        this.checkFolderid = this.moFolderid;
         // this.getData();
-      },
-    },
+      }
+    }
   },
   data() {
     return {
@@ -192,12 +244,12 @@ export default {
       limit: 10,
       total: 0,
       page: 1,
-      checkFolderid: "",
+      checkFolderid: ""
     };
   },
   computed: {
     getState() {
-      return function (item) {
+      return function(item) {
         if (item == "success") {
           return "成功";
         } else if (item == "failed") {
@@ -208,15 +260,15 @@ export default {
           return "待处理";
         } else if (item == "enriched") {
           return "enriched";
-        } else if(!item){
-          return "上传中"
+        } else if (!item) {
+          return "上传中";
         } else {
-          return item
+          return item;
         }
       };
     },
     getState2() {
-      return function (item) {
+      return function(item) {
         if (item == "success") {
           return "成功";
         } else if (item == "failed") {
@@ -227,18 +279,18 @@ export default {
           return "处理中";
         } else if (item == "enriched") {
           return "enriched";
-        } else if(!item){
-          return "上传中"
+        } else if (!item) {
+          return "上传中";
         } else {
-          return item
+          return item;
         }
       };
-    },
+    }
   },
   methods: {
     handleCurrentChange(val) {
-        this.page = val;
-        this.getData();
+      this.page = val;
+      this.getData();
     },
     getData() {
       this.isLoading = true;
@@ -247,25 +299,28 @@ export default {
         folderid: this.checkFolderid,
         n: this.fileName.trim(),
         page: this.page,
-        num: this.limit,
+        num: this.limit
       };
       this.ajax
         .post(this.$store.state.fileApi + "getFile", [params])
-        .then((res) => {
+        .then(res => {
           this.isLoading = false;
           console.log(res.data);
           this.total = res.data.result.length ? res.data.result[0].num : 0;
           this.fileArray = res.data.result;
           this.$parent.$refs.folder.getData();
         })
-        .catch((err) => {
+        .catch(err => {
           this.isLoading = false;
           console.error(err);
         });
     },
     handleSelectionChange(selectedRows) {
-      this.checkArray = selectedRows.map((row) => row.id);
-      this.checkArray2 = selectedRows.map(row => ({ id: row.id, documentid: row.documentid }));
+      this.checkArray = selectedRows.map(row => row.id);
+      this.checkArray2 = selectedRows.map(row => ({
+        id: row.id,
+        documentid: row.documentid
+      }));
     },
     addImg(e) {
       var el = e.currentTarget;
@@ -276,7 +331,7 @@ export default {
       event.preventDefault(); // 阻止默认的浏览器下载行为
       const files = event.dataTransfer.files;
       if (files.length) {
-        this.beforeUpload({target:{files}});
+        this.beforeUpload({ target: { files } });
       }
     },
     debouncedSearch() {
@@ -294,14 +349,25 @@ export default {
       let cfindex2 = 0;
       this.proVisible = true;
       const allowedExtensions = [
-        "csv","xls","xlsx","md","pdf","txt","ppt","pptx","docx"
+        "csv",
+        "xls",
+        "xlsx",
+        "md",
+        "pdf",
+        "txt",
+        "ppt",
+        "pptx",
+        "docx"
       ];
-      
-      const uploadFiles = async (files) => {
+
+      const uploadFiles = async files => {
         for (let cfindex = 0; cfindex < files.length; cfindex++) {
           file = files[cfindex];
-          const fileExtension = file.name.split('.').pop().toLowerCase();
-          
+          const fileExtension = file.name
+            .split(".")
+            .pop()
+            .toLowerCase();
+
           if (!allowedExtensions.includes(fileExtension)) {
             this.$message.error(`不支持的文件格式: ${file.name}`);
             await new Promise(resolve => setTimeout(resolve, 1000)); // 延迟1秒再跳过
@@ -311,11 +377,14 @@ export default {
           let formData = new FormData();
           const timestamp = Date.now();
           const baseName = file.name.slice(0, -(fileExtension.length + 1));
-          formData.append('file', new File([file], `${baseName}${timestamp}.${fileExtension}`));
-          formData.append('collection_ids', JSON.stringify([this.moFolderid]));
-          formData.append('id', uuid);
-          formData.append('metadata', JSON.stringify({title: file.name}));
-          formData.append('ingestion_mode', "fast");
+          formData.append(
+            "file",
+            new File([file], `${baseName}${timestamp}.${fileExtension}`)
+          );
+          formData.append("collection_ids", JSON.stringify([this.moFolderid]));
+          formData.append("id", uuid);
+          formData.append("metadata", JSON.stringify({ title: file.name }));
+          formData.append("ingestion_mode", "fast");
 
           // 使用同步方式上传文件
           await this.uploadFile(formData, file.name, uuid);
@@ -325,9 +394,11 @@ export default {
             await new Promise(resolve => setTimeout(resolve, 5000));
           }
         }
-        this.proVisible = false;
-        this.$message.success("上传成功");
-        this.getData(); // 在上传完文件后再调用getData
+        setTimeout(() => {
+          this.proVisible = false;
+          this.$message.success("操作完成");
+          this.getData(); // 在上传完所有文件后再调用getData
+        }, 1000);
       };
 
       await uploadFiles(event.target.files);
@@ -338,7 +409,7 @@ export default {
         // this.$store.state.fileApi + "upload"
         this.ajax.post("https://r2rserver.cocorobo.cn/v3/documents", formData, {
           headers: {
-            'Content-Type': 'multipart/form-data' 
+            "Content-Type": "multipart/form-data"
           }
         });
 
@@ -347,9 +418,11 @@ export default {
           did: uuid,
           uid: this.userid,
           fid: this.moFolderid,
-          mofid: "",
-        }
-        const res2 = await this.ajax.post(this.$store.state.api + 'addFile', [params]);
+          mofid: ""
+        };
+        const res2 = await this.ajax.post(this.$store.state.api + "addFile", [
+          params
+        ]);
       } catch (err) {
         console.error(err);
         this.$message.error("上传失败");
@@ -370,48 +443,66 @@ export default {
       this.$confirm("确定删除该文件吗?", "提示", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
-        type: "warning",
+        type: "warning"
       })
-       .then(() => {
-          let params = [{ 
-            ids: fileid,
-            documentids: documentid,
-          }];
+        .then(() => {
+          let params = [
+            {
+              ids: fileid,
+              documentids: documentid
+            }
+          ];
           this.ajax
-           .post(this.$store.state.fileApi + "deleteFile", params)
-           .then((res) => {
+            .post(this.$store.state.fileApi + "deleteFile", params)
+            .then(res => {
               this.$message({
                 message: "删除成功",
-                type: "success",
+                type: "success"
               });
-              this.checkArray = []
-              this.checkArray2 = []
+              this.checkArray = [];
+              this.checkArray2 = [];
               this.$refs.myTable.clearSelection();
               this.getData();
             })
-           .catch((err) => {
+            .catch(err => {
               this.$message.error("删除失败");
               console.error(err);
             });
         })
-       .catch(() => {});
+        .catch(() => {});
     },
-    batchDelete(){
-      if(!this.checkArray.length){
-        this.$message.warning("请选择要删除的文件")
+    batchDelete() {
+      if (!this.checkArray.length) {
+        this.$message.warning("请选择要删除的文件");
         return;
       }
       let array = this.checkArray2.map(item => item.id);
       let array2 = this.checkArray2.map(item => item.documentid);
-      this.deleteFile(array.join(","), array2.join(","))
+      this.deleteFile(array.join(","), array2.join(","));
     },
-    check(row){
+    checkFile(row) {
       this.$refs.checkDialog.openG(row.documentid);
+    },
+    extractFile(row) {
+      let params = {
+        documentid: row.documentid
+      };
+      // 获取切片
+      try {
+        const res = this.ajax.post(this.$store.state.fileApi + "extractFile2", [
+          params
+        ]);
+        console.log(res.data);
+        this.$message.success("提取文件中");
+        this.getData();
+      } catch (err) {
+        console.error(err);
+      }
     }
   },
   mounted() {
     this.getData();
-  },
+  }
 };
 </script>
 
@@ -491,18 +582,18 @@ export default {
 
 .f_box_top_right > .input {
   position: relative;
-  width: 250px;
-  height: 35px;
+  width: 280px;
+  height: 40px;
 }
 
 .f_box_top_right > .input > input {
   width: 100%;
   height: 100%;
-  border: 1px solid #d9d9d9;
+  border: 1px solid #dcdfe6;
   border-radius: 5px;
-  padding: 0 45px 0 10px;
+  padding: 0 45px 0 15px;
   box-sizing: border-box;
-  font-size: 12px;
+  font-size: 14px;
   outline: none;
 }
 
@@ -845,4 +936,4 @@ export default {
   white-space: nowrap;
   text-overflow: ellipsis;
 }
-</style>
+</style>

+ 10 - 11
src/components/pages/knowledge/folder.vue

@@ -7,12 +7,11 @@
         </div>
       </div>
       <div class="f_box_top_right">
-        <div class="btn" @click="openAdd">
-          新建文件夹
-        </div>
-        <div class="btn delBtn" @click="deleteFolder">
-          删除
-        </div>
+        <el-button type="primary" size="small" @click="openAdd"
+          >新建文件夹</el-button>
+        <el-button type="danger" size="small" @click="deleteFolder"
+          >删除</el-button
+        >
       </div>
     </div>
     <div class="f_box_top">
@@ -594,18 +593,18 @@ export default {
 
 .f_box_top_right > .input {
   position: relative;
-  width: 250px;
-  height: 35px;
+  width: 280px;
+  height: 40px;
 }
 
 .f_box_top_right > .input > input {
   width: 100%;
   height: 100%;
-  border: 1px solid #d9d9d9;
+  border: 1px solid #dcdfe6;
   border-radius: 5px;
-  padding: 0 45px 0 10px;
+  padding: 0 45px 0 15px;
   box-sizing: border-box;
-  font-size: 12px;
+  font-size: 14px;
   outline: none;
 }
 

+ 15 - 5
src/components/pages/knowledge/folderDetail.vue

@@ -15,13 +15,18 @@
         <div class="t_title" v-else>{{ data.name }}</div>
         <div class="t_btn">
           <!-- <div class="btn">复制文件夹</div> -->
-          <div
+                  <!-- <el-button type="primary" size="small"
+          >复制文件夹</el-button> -->
+        <el-button  v-if="userid == data.userid && data.isMo == '2'" type="danger" size="small" @click="deleteFile(pid.split('/')[1])"
+          >删除文件夹</el-button
+        >
+          <!-- <div
             class="btn"
             @click="deleteFile(pid.split('/')[1])"
             v-if="userid == data.userid && data.isMo == '2'"
           >
             删除文件夹
-          </div>
+          </div> -->
         </div>
       </div>
       <div class="detail">
@@ -359,13 +364,18 @@ export default {
     },
     handleTagChange(value) {
       const tagArray = this.tagData3;
-      const missingTags = value.filter(
+      const selectedTags = value.flatMap(tag => tag.split(/[,,]/).map(t => t.trim()));
+
+      // 去掉选中反而选中不带逗号的值
+      const uniqueTags = [...new Set(selectedTags.map(tag => tag.replace(/[,,].*/, '').trim()))];
+
+      const missingTags = uniqueTags.filter(
         selectedTag => !tagArray.some(tag => tag.name === selectedTag)
       );
 
-      console.log("Selected tags:", value);
+      console.log("Selected tags:", uniqueTags);
       console.log("Missing tags:", missingTags);
-
+      this.check3 = uniqueTags
       missingTags.forEach(tag => this.addTag(tag));
       this.updateFolder();
     },

+ 228 - 110
src/components/pages/knowledge/folderFileBox.vue

@@ -5,7 +5,32 @@
         <div class="f_box_top_title">文件</div>
       </div>
       <div class="f_box_top_right">
-        <div class="btn" @click="serchFile">刷新</div>
+        <el-button type="primary" size="small" @click="serchFile"
+          >刷新</el-button
+        >
+        <el-button type="primary" size="small" @click="addImg($event)"
+          >上传文件
+          <input
+            type="file"
+            accept="*"
+            style="display: none"
+            multiple="multiple"
+            @change="beforeUpload($event)"
+        /></el-button>
+        <el-button
+          type="primary"
+          size="small"
+          @click="openG"
+          v-if="userid == data.userid && data.isMo == '2'"
+          >关联文件</el-button
+        >
+        <el-button type="primary" size="small" @click="batchRemove"
+          >移除</el-button
+        >
+        <el-button type="danger" size="small" @click="batchDelete"
+          >删除</el-button
+        >
+        <!-- <div class="btn" @click="serchFile">刷新</div>
         <div class="btn" @click="addImg($event)">
           上传文件
           <input
@@ -15,10 +40,10 @@
             multiple="multiple"
             @change="beforeUpload($event)"
           />
-        </div>
-        <div class="btn" @click="openG" v-if="userid == data.userid && data.isMo == '2'">关联文件</div>
+        </div> -->
+        <!-- <div class="btn" @click="openG" v-if="userid == data.userid && data.isMo == '2'">关联文件</div>
         <div class="btn" @click="batchRemove">移除</div>
-        <div class="btn delBtn" @click="batchDelete">删除</div>
+        <div class="btn delBtn" @click="batchDelete">删除</div> -->
       </div>
     </div>
     <div class="f_box_top">
@@ -32,7 +57,7 @@
             @input="debouncedSearch"
           />
           <div class="serch"></div>
-           <!-- @click="serchFile" -->
+          <!-- @click="serchFile" -->
         </div>
       </div>
     </div>
@@ -50,39 +75,89 @@
           style="width: 100%"
           @selection-change="handleSelectionChange"
           ref="myTable"
-          :row-key="(row) => row.id"
+          :row-key="row => row.id"
         >
           <el-table-column
             type="selection"
             width="50px"
             :reserve-selection="true"
           ></el-table-column>
-          <el-table-column prop="name" label="文件名" show-overflow-tooltip min-width="15"></el-table-column>
-          <el-table-column prop="username" label="拥有者" show-overflow-tooltip min-width="20"></el-table-column>
-          <el-table-column prop="folderName" label="所属文件夹" show-overflow-tooltip min-width="20"></el-table-column>
+          <el-table-column
+            prop="name"
+            label="文件名"
+            show-overflow-tooltip
+            min-width="15"
+          ></el-table-column>
+          <el-table-column
+            prop="username"
+            label="拥有者"
+            show-overflow-tooltip
+            min-width="20"
+          ></el-table-column>
+          <el-table-column
+            prop="folderName"
+            label="所属文件夹"
+            show-overflow-tooltip
+            min-width="20"
+          ></el-table-column>
           <el-table-column label="处理状态" width="80px">
             <template slot-scope="scope">
               {{ getState(scope.row.ingestionStatus) }}
             </template>
           </el-table-column>
-           <el-table-column label="是否已提取知识图谱" width="150px">
+          <el-table-column label="是否提取" width="80px">
             <template slot-scope="scope">
               {{ getState2(scope.row.extractionStatus) }}
             </template>
           </el-table-column>
-          <el-table-column label="文件类型" show-overflow-tooltip min-width="10">
-             <template slot-scope="scope">
-              {{ scope.row.documentType ? scope.row.documentType : '-' }}
+          <el-table-column
+            label="文件类型"
+            show-overflow-tooltip
+            min-width="10"
+          >
+            <template slot-scope="scope">
+              {{ scope.row.documentType ? scope.row.documentType : "-" }}
             </template>
           </el-table-column>
           <!-- <el-table-column label="公开状态" show-overflow-tooltip width="80px"></el-table-column> -->
-          <el-table-column prop="time" label="上传时间" show-overflow-tooltip min-width="20"></el-table-column>
-          <el-table-column label="操作" width="250px">
+          <el-table-column
+            prop="time"
+            label="上传时间"
+            show-overflow-tooltip
+            min-width="20"
+          ></el-table-column>
+          <el-table-column label="操作" width="300px">
             <template slot-scope="scope">
               <div>
-                <el-button type="primary" size="small" @click="check(scope.row)" :disabled="scope.row.ingestionStatus == 'failed'">查看</el-button>
-                <el-button type="primary" size="small" v-if="userid == data.userid && data.isMo == '2'" @click="removeFile(scope.row.id, scope.row.documentid)">移除</el-button>
-                <el-button type="danger" size="small" v-if="userid == scope.row.userid" @click="deleteFile(scope.row.id, scope.row.documentid)">删除</el-button>
+                             <el-button
+                  type="primary"
+                  size="small"
+                  @click="extractFile(scope.row)"
+                  v-if="userid == data.userid "
+                  :disabled="scope.row.ingestionStatus == 'failed'"
+                  >提取</el-button
+                >
+                <el-button
+                  type="primary"
+                  size="small"
+                  @click="checkFile(scope.row)"
+                  :disabled="scope.row.ingestionStatus == 'failed'"
+                  >查看</el-button
+                >
+                <el-button
+                  type="primary"
+                  size="small"
+                  v-if="userid == data.userid && data.isMo == '2'"
+                  @click="removeFile(scope.row.id, scope.row.documentid)"
+                  >移除</el-button
+                >
+                <el-button
+                  type="danger"
+                  size="small"
+                  v-if="userid == scope.row.userid"
+                  @click="deleteFile(scope.row.id, scope.row.documentid)"
+                  >删除</el-button
+                >
               </div>
             </template>
           </el-table-column>
@@ -129,8 +204,8 @@ import wpdf from "../test/file/wPdf2.vue";
 import wOffice from "../test/file/wOffice.vue";
 
 import { v4 as uuidv4 } from "uuid";
-import relateFiles from './components/relateFiles'
-import checkDialog from './components/checkDialog'
+import relateFiles from "./components/relateFiles";
+import checkDialog from "./components/checkDialog";
 export default {
   components: {
     wVideo,
@@ -141,22 +216,22 @@ export default {
   },
   props: {
     userid: {
-      type: String,
+      type: String
     },
     typeArray: {
-      type: Array,
+      type: Array
     },
     pid: {
-      type: String,
+      type: String
     },
     folderid: {
-      type: String,
+      type: String
     },
     moFolderid: {
-      type: String,
+      type: String
     },
     data: {
-      type: Object,
+      type: Object
     }
   },
   watch: {
@@ -167,11 +242,11 @@ export default {
         this.fileArray = [];
         this.checkArray = [];
         this.fileName = "";
-        this.proVisible = false
-        this.page = 1
+        this.proVisible = false;
+        this.page = 1;
         this.getData();
-      },
-    },
+      }
+    }
   },
   data() {
     return {
@@ -198,12 +273,12 @@ export default {
       limit: 10,
       total: 0,
       page: 1,
-      debounceTimeout: null,
+      debounceTimeout: null
     };
   },
   computed: {
-     getState() {
-      return function (item) {
+    getState() {
+      return function(item) {
         if (item == "success") {
           return "成功";
         } else if (item == "failed") {
@@ -214,15 +289,15 @@ export default {
           return "待处理";
         } else if (item == "enriched") {
           return "enriched";
-        } else if(!item){
-          return "上传中"
+        } else if (!item) {
+          return "上传中";
         } else {
-          return item
+          return item;
         }
       };
     },
     getState2() {
-      return function (item) {
+      return function(item) {
         if (item == "success") {
           return "成功";
         } else if (item == "failed") {
@@ -233,13 +308,13 @@ export default {
           return "处理中";
         } else if (item == "enriched") {
           return "enriched";
-        } else if(!item){
-          return "上传中"
+        } else if (!item) {
+          return "上传中";
         } else {
-          return item
+          return item;
         }
       };
-    },
+    }
   },
   methods: {
     handleCurrentChange(val) {
@@ -253,24 +328,27 @@ export default {
         folderid: this.folderid,
         n: this.fileName.trim(),
         page: this.page,
-        num: this.limit,
+        num: this.limit
       };
       this.ajax
         .post(this.$store.state.fileApi + "getFile", [params])
-        .then((res) => {
+        .then(res => {
           this.isLoading = false;
           console.log(res.data);
           this.total = res.data.result.length ? res.data.result[0].num : 0;
           this.fileArray = res.data.result;
         })
-        .catch((err) => {
+        .catch(err => {
           this.isLoading = false;
           console.error(err);
         });
     },
     handleSelectionChange(selectedRows) {
-      this.checkArray = selectedRows.map((row) => row.id);
-      this.checkArray2 = selectedRows.map(row => ({ id: row.id, documentid: row.documentid }));
+      this.checkArray = selectedRows.map(row => row.id);
+      this.checkArray2 = selectedRows.map(row => ({
+        id: row.id,
+        documentid: row.documentid
+      }));
     },
     addImg(e) {
       var el = e.currentTarget;
@@ -281,7 +359,7 @@ export default {
       event.preventDefault(); // 阻止默认的浏览器下载行为
       const files = event.dataTransfer.files;
       if (files.length) {
-        this.beforeUpload({target:{files}});
+        this.beforeUpload({ target: { files } });
       }
     },
     debouncedSearch() {
@@ -299,18 +377,29 @@ export default {
       let cfindex2 = 0;
       this.proVisible = true;
       const allowedExtensions = [
-        "csv","xls","xlsx","md","pdf","txt","ppt","pptx","docx"
+        "csv",
+        "xls",
+        "xlsx",
+        "md",
+        "pdf",
+        "txt",
+        "ppt",
+        "pptx",
+        "docx"
       ];
       //     let uuid = uuidv4();
       // let res = window.uploadFile({ file: event.target.files[0], uuid, userid:this.userid, folderid:this.folderid, moFolderid: this.moFolderid })
       // console.log(res);
-      
+
       // return
-      const uploadFiles = async (files) => {
+      const uploadFiles = async files => {
         for (let cfindex = 0; cfindex < files.length; cfindex++) {
           file = files[cfindex];
-          const fileExtension = file.name.split('.').pop().toLowerCase();
-          
+          const fileExtension = file.name
+            .split(".")
+            .pop()
+            .toLowerCase();
+
           if (!allowedExtensions.includes(fileExtension)) {
             this.$message.error(`不支持的文件格式: ${file.name}`);
             await new Promise(resolve => setTimeout(resolve, 1000)); // 延迟1秒再跳过
@@ -322,11 +411,17 @@ export default {
           const timestamp = Date.now();
           const baseName = file.name.slice(0, -(fileExtension.length + 1));
           let string = [this.folderid, this.moFolderid].filter(id => id);
-          formData.append('file', new File([file], `${baseName}${timestamp}.${fileExtension}`));
-          formData.append('collection_ids', JSON.stringify(string));
-          formData.append('id', uuid);
-          formData.append('metadata', JSON.stringify({title: file.name, collection_ids: string}));
-          formData.append('ingestion_mode', "fast");
+          formData.append(
+            "file",
+            new File([file], `${baseName}${timestamp}.${fileExtension}`)
+          );
+          formData.append("collection_ids", JSON.stringify(string));
+          formData.append("id", uuid);
+          formData.append(
+            "metadata",
+            JSON.stringify({ title: file.name, collection_ids: string })
+          );
+          formData.append("ingestion_mode", "fast");
 
           // 使用同步方式上传文件
           await this.uploadFile(formData, file.name, uuid);
@@ -336,9 +431,11 @@ export default {
             await new Promise(resolve => setTimeout(resolve, 5000));
           }
         }
-        this.proVisible = false;
-        this.$message.success("上传成功");
-        this.getData(); // 在上传完所有文件后再调用getData
+        setTimeout(() => {
+          this.proVisible = false;
+          this.$message.success("操作完成");
+          this.getData(); // 在上传完所有文件后再调用getData
+        }, 1000);
       };
 
       await uploadFiles(event.target.files);
@@ -347,7 +444,7 @@ export default {
       try {
         this.ajax.post("https://r2rserver.cocorobo.cn/v3/documents", formData, {
           headers: {
-            'Content-Type': 'multipart/form-data'
+            "Content-Type": "multipart/form-data"
           }
         });
 
@@ -359,7 +456,9 @@ export default {
           fid: this.folderid,
           mofid: this.moFolderid != this.folderid ? this.moFolderid : ""
         };
-        const res2 = await this.ajax.post(this.$store.state.api + 'addFile', [params]);
+        const res2 = await this.ajax.post(this.$store.state.api + "addFile", [
+          params
+        ]);
 
         console.log(res2);
       } catch (err) {
@@ -382,94 +481,113 @@ export default {
       this.$confirm("确定删除该文件吗?", "提示", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
-        type: "warning",
+        type: "warning"
       })
-       .then(() => {
-          let params = [{ 
-            ids: fileid,
-            documentids: documentid,
-          }];
+        .then(() => {
+          let params = [
+            {
+              ids: fileid,
+              documentids: documentid
+            }
+          ];
           this.ajax
-           .post(this.$store.state.fileApi + "deleteFile", params)
-           .then((res) => {
+            .post(this.$store.state.fileApi + "deleteFile", params)
+            .then(res => {
               this.$message({
                 message: "删除成功",
-                type: "success",
+                type: "success"
               });
-              this.checkArray = []
-              this.checkArray2 = []
+              this.checkArray = [];
+              this.checkArray2 = [];
               this.$refs.myTable.clearSelection();
               this.getData();
             })
-           .catch((err) => {
+            .catch(err => {
               this.$message.error("删除失败");
               console.error(err);
             });
         })
-       .catch(() => {});
+        .catch(() => {});
     },
-    batchDelete(){
-      if(!this.checkArray.length){
-        this.$message.warning("请选择要删除的文件")
+    batchDelete() {
+      if (!this.checkArray.length) {
+        this.$message.warning("请选择要删除的文件");
         return;
       }
       let array = this.checkArray2.map(item => item.id);
       let array2 = this.checkArray2.map(item => item.documentid);
-      this.deleteFile(array.join(","), array2.join(","))
+      this.deleteFile(array.join(","), array2.join(","));
     },
-    removeFile(fileid, documentid){
+    removeFile(fileid, documentid) {
       this.$confirm("确定移除该文件吗?", "提示", {
         confirmButtonText: "确定",
         cancelButtonText: "取消",
-        type: "warning",
+        type: "warning"
       })
-       .then(() => {
-          let params = [{ 
-            ids: fileid,
-            documentids: documentid,
-            folderid: this.folderid
-          }];
+        .then(() => {
+          let params = [
+            {
+              ids: fileid,
+              documentids: documentid,
+              folderid: this.folderid
+            }
+          ];
           this.ajax
-           .post(this.$store.state.fileApi + "removeFile", params)
-           .then((res) => {
+            .post(this.$store.state.fileApi + "removeFile", params)
+            .then(res => {
               this.$message({
                 message: "移除成功",
-                type: "success",
+                type: "success"
               });
-              this.checkArray = []
-              this.checkArray2 = []
+              this.checkArray = [];
+              this.checkArray2 = [];
               this.$refs.myTable.clearSelection();
               this.getData();
             })
-           .catch((err) => {
+            .catch(err => {
               this.$message.error("移除失败");
               console.error(err);
             });
         })
-       .catch(() => {});
+        .catch(() => {});
     },
-    batchRemove(){
-      if(!this.checkArray.length){
-        this.$message.warning("请选择要移除的文件")
+    batchRemove() {
+      if (!this.checkArray.length) {
+        this.$message.warning("请选择要移除的文件");
         return;
       }
       let array = this.checkArray2.map(item => item.id);
       let array2 = this.checkArray2.map(item => item.documentid);
-      this.removeFile(array.join(","), array2.join(","))
+      this.removeFile(array.join(","), array2.join(","));
     },
     isSelectable(row) {
-      return row.isMo !== '2';
+      return row.isMo !== "2";
     },
-    openG(){
-      this.$refs.relateFiles.openG(this.userid, this.folderid, this.moFolderid)
+    openG() {
+      this.$refs.relateFiles.openG(this.userid, this.folderid, this.moFolderid);
     },
-    check(row){
+    checkFile(row) {
       this.$refs.checkDialog.openG(row.documentid);
+    },
+    extractFile(row) {
+      let params = {
+        documentid: row.documentid
+      };
+      // 获取切片
+      try {
+        const res = this.ajax.post(this.$store.state.fileApi + "extractFile2", [
+          params
+        ]);
+        console.log(res.data);
+        this.$message.success("提取文件中");
+        this.getData();
+      } catch (err) {
+        console.error(err);
+      }
     }
   },
   mounted() {
     // this.getData();
-
     //  const script = document.createElement('script');
     // script.src = 'https://beta.cloud.cocorobo.cn/js/Common/uploadR2R.js';
     // script.type = 'text/javascript';
@@ -482,7 +600,7 @@ export default {
     //   console.error('上传脚本加载失败');
     // };
     // document.head.appendChild(script);
-  },
+  }
 };
 </script>
 
@@ -562,18 +680,18 @@ export default {
 
 .f_box_top_right > .input {
   position: relative;
-  width: 250px;
-  height: 35px;
+  width: 280px;
+  height: 40px;
 }
 
 .f_box_top_right > .input > input {
   width: 100%;
   height: 100%;
-  border: 1px solid #d9d9d9;
+  border: 1px solid #dcdfe6;
   border-radius: 5px;
-  padding: 0 45px 0 10px;
+  padding: 0 45px 0 15px;
   box-sizing: border-box;
-  font-size: 12px;
+  font-size: 14px;
   outline: none;
 }
 
@@ -916,4 +1034,4 @@ export default {
   white-space: nowrap;
   text-overflow: ellipsis;
 }
-</style>
+</style>

+ 1 - 1
src/components/pages/knowledge/index.vue

@@ -347,7 +347,7 @@ export default {
 
 
 .l_type_box>.nav>.nav_b>.nav_child_box>.nav_box>.name {
-  font-size: 16px;
+  font-size: 14px;
   color: #000000e6;
   white-space: nowrap;
   overflow: hidden;