瀏覽代碼

知识库添加目录

yangg 9 月之前
父節點
當前提交
b61965ae07

+ 1 - 1
.env.development

@@ -2,5 +2,5 @@
 ENV = 'development'
 port = 8080
 # base api
-VUE_APP_BASE_API = 'http://58.246.234.210:8084'
+VUE_APP_BASE_API = 'http://192.168.1.178:8084'
 #192.168.1.178

File diff suppressed because it is too large
+ 0 - 0
dist/index.html


+ 0 - 1
dist/static/css/chunk-0d23420e.8d28e945.css

@@ -1 +0,0 @@
-.data-search{padding:20px 10px 0 10px}.custom-upload .el-upload,.custom-upload .el-upload .el-upload-dragger{width:100%}.data-list .btns,.data-list .page-info{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row}.data-list .page-info{margin-top:10px}.data-list .page-info .opt{width:50%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:start;-ms-flex-pack:start;justify-content:flex-start;-webkit-box-align:center;-ms-flex-align:center;align-items:center}.data-list .page-info .opt .pick{width:100px}.data-list .page-info .opt .section{margin-left:10px}.data-list .page-info .page-data{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;width:100%}.data-list .page-info .page-data .page-item{width:100%}.data-list .page-info .page-data .page-opt{width:100%;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:end;-ms-flex-pack:end;justify-content:flex-end}.project-search{padding:30px;font-size:12px}

File diff suppressed because it is too large
+ 0 - 0
dist/static/css/chunk-68954024.60511509.css


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/app.da01ba0a.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/chunk-0d23420e.98251092.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/chunk-68954024.91665729.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/chunk-b4ab3520.5e3e414d.js


File diff suppressed because it is too large
+ 0 - 0
dist/static/js/chunk-e6bc7406.2190a503.js


+ 50 - 1
src/api/knowledge.js

@@ -119,4 +119,53 @@ export function getRunStatus(data) {
     method: 'post',
     data
   })
-}
+}
+
+/* 目录列表 */
+export function selectTypeList(data) {
+  return request({
+    url: '/docType/selectTypeList',
+    method: 'post',
+    data
+  })
+}
+
+/* 新增目录 */
+
+export function insertType(data) {
+  return request({
+    url: '/docType/insertType',
+    method: 'post',
+    data
+  })
+}
+
+/* 编辑目录 */
+
+export function updateType(data) {
+  return request({
+    url: '/docType/updateType',
+    method: 'post',
+    data
+  })
+}
+/* 查看目录 */
+
+export function Info(data) {
+  return request({
+    url: '/docType/Info',
+    method: 'post',
+    data
+  })
+}
+
+/* 删除目录 */
+
+export function deleteType(data) {
+  return request({
+    url: '/docType/delete',
+    method: 'post',
+    data
+  })
+}
+

+ 1023 - 44
src/views/knowledgeMenu/category/knowledgeSet.vue

@@ -1,61 +1,1040 @@
 <template>
-    <div class="project-search">
+  <div class="project-search">
+    <div class="file-categories">
+      <el-button type="primary" @click="createNewFolder">
+        <i class="el-icon-plus"></i> 新建目录
+      </el-button>
+      <el-tree
+        :data="folders"
+        :props="defaultProps"
+        @node-click="handleNodeClick"
+        ref="folderTree"
+        :default-expanded-keys="['0']"
+        node-key="id"
+        highlight-current
+      >
+        <span class="custom-tree-node" slot-scope="{ node, data }">
+          <span>
+            <i class="el-icon-folder"></i>
+            {{ node.label }}
+          </span>
+          <span class="file-count">{{ data.document_count }} files</span>
+          <span class="folder-actions" v-if="data.id !== '0'">
+            <el-tooltip
+              class="item"
+              effect="dark"
+              content="编辑"
+              placement="top"
+            >
+              <i class="el-icon-edit" @click.stop="editFolder(data)"></i>
+            </el-tooltip>
+            <el-tooltip
+              class="item"
+              effect="dark"
+              content="删除"
+              placement="top"
+            >
+              <i class="el-icon-delete" @click.stop="deleteFolder(data.id)"></i>
+            </el-tooltip>
+          </span>
+        </span>
+      </el-tree>
+    </div>
+    <div class="right">
       <dataSearch @bindSetQuery="setQuery"></dataSearch>
-      <dataList
+      <!--  <dataList
         :queryForm="queryForm"
         :allowEdit="checkAuth('/document/update')"
         :allowDelete="checkAuth('/document/delete')"
-      ></dataList>
+      ></dataList> -->
+      <div
+        class="data-list"
+        v-loading="loading"
+        element-loading-text="加载中..."
+      >
+        <div class="batch-actions" style="margin: 10px 0">
+          <el-button
+            type="primary"
+            @click="batchAnalysis"
+            :disabled="!selectedRows.length"
+            >批量解析</el-button
+          >
+        </div>
+        <el-table
+          ref="dataTable"
+          :data="dataList"
+          style="width: 100%"
+          header-row-class-name="headerBg"
+          empty-text="没有模板信息"
+          @selection-change="handleSelectionChange"
+          @sort-change="handleSortChange"
+        >
+          <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="文件名称"
+            align="center"
+            sortable="custom"
+          >
+            <template #default="scope">
+              <span>{{ scope.row.name }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="type" label="文件类型" align="center" />
+          <el-table-column prop="chunk_num" label="分块数" align="center" />
+          <el-table-column
+            prop="create_time"
+            label="上传时间"
+            align="center"
+            sortable="custom"
+          />
+          <el-table-column prop="run" label="解析状态" align="center">
+            <template #default="scope">
+              <div v-if="scope.row.run == 0">未解析</div>
+              <div v-if="scope.row.run == 1">解析中</div>
+              <div v-if="scope.row.run == 3">成功</div>
+              <div v-if="scope.row.run == 4">失败</div>
+              <div v-if="scope.row.run == 5">待处理</div>
+              <div v-if="scope.row.run == 6">文件异常</div>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" width="330">
+            <template #default="scope">
+              <div class="btns">
+                <el-tooltip
+                  class="item"
+                  effect="dark"
+                  content="预览"
+                  placement="top"
+                >
+                  <el-button
+                    type="text"
+                    size="small"
+                    circle
+                    icon="el-icon-view"
+                    v-if="checkAuth('/document/update')"
+                    style="font-size: 20px"
+                    @click="getName(scope.row)"
+                  ></el-button
+                ></el-tooltip>
+                <el-tooltip
+                  class="item"
+                  effect="dark"
+                  :content="
+                    scope.row.run === 1 || scope.row.run === 5
+                      ? '解析中'
+                      : '解析'
+                  "
+                  placement="top"
+                >
+                  <el-button
+                    v-if="scope.row.run !== 1 && scope.row.run !== 5"
+                    type="text"
+                    icon="el-icon-caret-right"
+                    circle
+                    @click="analysis(scope.row)"
+                    style="font-size: 20px"
+                  >
+                  </el-button>
+                  <el-button
+                    v-else
+                    type="text"
+                    icon="el-icon-loading"
+                    circle
+                    :disabled="true"
+                    style="font-size: 20px"
+                  >
+                  </el-button>
+                </el-tooltip>
+                <el-tooltip
+                  class="item"
+                  effect="dark"
+                  content="解析方法"
+                  placement="top"
+                >
+                  <el-button
+                    type="text"
+                    size="small"
+                    circle
+                    icon="el-icon-tickets"
+                    v-if="checkAuth('/document/update')"
+                    style="font-size: 20px"
+                    @click="Analytical(scope.row)"
+                  ></el-button
+                ></el-tooltip>
+                <el-tooltip
+                  class="item"
+                  effect="dark"
+                  content="修改文件名"
+                  placement="top"
+                >
+                  <el-button
+                    type="text"
+                    size="small"
+                    circle
+                    icon="el-icon-edit"
+                    v-if="checkAuth('/document/update')"
+                    style="font-size: 20px"
+                    @click="btnEdit(scope.row)"
+                  ></el-button
+                ></el-tooltip>
+                <el-tooltip
+                  class="item"
+                  effect="dark"
+                  content="下载"
+                  placement="top"
+                >
+                  <el-button
+                    type="text"
+                    size="small"
+                    circle
+                    icon="el-icon-download"
+                    @click="btnDown(scope.row)"
+                    :loading="scope.row.downloading"
+                    style="font-size: 20px"
+                  ></el-button
+                ></el-tooltip>
+                <el-tooltip
+                  class="item"
+                  effect="dark"
+                  content="删除"
+                  placement="top"
+                >
+                  <el-button
+                    type="text"
+                    size="small"
+                    circle
+                    icon="el-icon-delete"
+                    v-if="checkAuth('/document/delete')"
+                    @click="btnDelete(scope.row.id)"
+                    style="font-size: 20px"
+                  ></el-button
+                ></el-tooltip>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <div class="page-info">
+          <el-pagination
+            :currentPage="queryForm.page"
+            :page-size="queryForm.pageSize"
+            :total="recordCount"
+            :page-count="pageTotal"
+            :page-sizes="[10, 20, 50, 100]"
+            background
+            layout="sizes, prev, pager, next"
+            @current-change="ChangePage"
+            @size-change="handleSizeChange"
+          >
+          </el-pagination>
+        </div>
+        <el-dialog
+          title="修改名称"
+          :visible.sync="dialogVisible"
+          width="30%"
+          :before-close="handleClose"
+        >
+          <el-form :model="wordForm" ref="wordRef" :rules="wordRules">
+            <el-form-item label="文件名称:" prop="new_name">
+              <el-input
+                v-model="wordForm.new_name"
+                placeholder="请输入文件名称"
+              ></el-input>
+            </el-form-item>
+          </el-form>
+          <span slot="footer" class="dialog-footer">
+            <el-button @click="handleClose">取 消</el-button>
+            <el-button type="primary" @click="submitBuck">确 定</el-button>
+          </span>
+        </el-dialog>
+
+        <!-- 解析方法 -->
+        <el-dialog
+          title="解析方法"
+          :visible.sync="anaDialogVisible"
+          width="30%"
+          :before-close="handleAnalClose"
+        >
+          <el-form
+            :model="analForm"
+            ref="analRef"
+            label-position="top"
+            :rules="analRules"
+          >
+            <el-form-item label="页码范围:" prop="new_name">
+              <el-input-number
+                v-model="analForm.start_page"
+                controls-position="right"
+                :min="1"
+              ></el-input-number>
+              <el-input-number
+                v-model="analForm.end_page"
+                controls-position="right"
+                :min="1"
+                style="margin-left: 10px"
+              ></el-input-number>
+            </el-form-item>
+            <el-form-item label="块token数">
+              <div class="block">
+                <el-slider v-model="analForm.token_num" :max="2048" show-input>
+                </el-slider>
+              </div>
+            </el-form-item>
+          </el-form>
+          <span slot="footer" class="dialog-footer">
+            <el-button @click="handleAnalClose">取 消</el-button>
+            <el-button type="primary" @click="submitAnal">确 定</el-button>
+          </span>
+        </el-dialog>
+        <el-dialog
+          title="新建目录"
+          :visible.sync="newFolderDialogVisible"
+          width="30%"
+        >
+          <el-form
+            :model="newFolderForm"
+            :rules="newFolderRules"
+            ref="newFolderFormRef"
+          >
+            <el-form-item label="文件夹名称" prop="name">
+              <el-input v-model="newFolderForm.name"></el-input>
+            </el-form-item>
+          </el-form>
+          <span slot="footer" class="dialog-footer">
+            <el-button @click="newFolderDialogVisible = false">取 消</el-button>
+            <el-button type="primary" @click="submitNewFolder">确 定</el-button>
+          </span>
+        </el-dialog>
+      </div>
     </div>
-  </template>
+  </div>
+</template>
     
     <script>
-  import { mapGetters } from "vuex";
-  import { dataSearch, dataList } from "./components";
-  export default {
-    components: {
-      dataSearch,
-      dataList,
-    },
-    computed: {
-      ...mapGetters(["roleInfo", "authList"]),
-    },
-  
-    data() {
-      return {
-        queryForm: {
-          page: 1,
-          pageSize: 10,
-          bucket_id:''
+import { mapGetters } from "vuex";
+import { dataSearch, dataList } from "./components";
+import {
+  getBucketContents,
+  deleteFile,
+  nameGetUrl,
+  renameFile,
+  analysis,
+  batchAnalysis,
+  getRunStatus,
+  insertType,
+  updateType,
+  selectTypeList,
+  deleteType,
+  Info,
+} from "@/api/knowledge";
+export default {
+  components: {
+    dataSearch,
+    dataList,
+  },
+  computed: {
+    ...mapGetters(["roleInfo", "authList"]),
+  },
+
+  data() {
+    return {
+      queryForm: {
+        page: 1,
+        pageSize: 10,
+        bucket_id: "",
+        folder: "0",
+      },
+      typeForm: {
+        page: 1,
+        pageSize: 10,
+        kb_id: "",
+      },
+      folders: [{ id: "0", name: "All", document_count: 0 }],
+      defaultProps: {
+        children: "children",
+        label: "name",
+      },
+
+      // dataList 组件的数据
+      loading: false,
+      dialogVisible: false,
+      currentDataId: 0,
+      recordCount: 0,
+      pageTotal: 1,
+      dataList: [],
+      currentData: {},
+      wordForm: {
+        document_id: "",
+        new_name: "",
+      },
+      wordRules: {
+        new_name: [
+          { required: true, message: "请输入文件名称", trigger: "blur" },
+        ],
+      },
+      anaDialogVisible: false,
+      analForm: {
+        start_page: 1,
+        end_page: 10000,
+        token_num: 0,
+      },
+      analRules: {},
+      selectedRows: [],
+      sortColumn: "",
+      sortOrder: "",
+      analysisStatusCheckers: {},
+      newFolderDialogVisible: false,
+      newFolderForm: {
+        name: "",
+        kb_id: "",
+        status: 5,
+      },
+      newFolderRules: {
+        name: [
+          { required: true, message: "请输入文件夹名称", trigger: "blur" },
+        ],
+      },
+    };
+  },
+  mounted() {
+    this.queryForm.bucket_id = this.$route.query.id;
+    this.typeForm.kb_id = this.$route.query.id;
+    this.$nextTick(() => {
+      this.initializeTree();
+    });
+    this.typeList();
+    this.search();
+  },
+  beforeDestroy() {
+    Object.values(this.analysisStatusCheckers).forEach(clearTimeout);
+  },
+  methods: {
+    editFolder(folder) {
+      this.$prompt("请输入新的文件夹名称", "编辑文件夹", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        inputValue: folder.name,
+        inputValidator: (value) => {
+          if (!value) {
+            return "文件夹名称不能为空";
+          }
+          return true;
         },
-      };
+      })
+        .then(({ value }) => {
+          if (value !== folder.name) {
+            const updatedFolder = { ...folder, name: value };
+            updateType(updatedFolder)
+              .then((res) => {
+                if (res.status === 200) {
+                  this.$message.success("更新文件夹成功");
+                  this.typeList(); // 刷新文件夹列表
+                } else {
+                  this.$message.error("更新文件夹失败");
+                }
+              })
+              .catch((error) => {
+                console.error("更新文件夹时出错:", error);
+                this.$message.error("更新文件夹时出错");
+              });
+          }
+        })
+        .catch(() => {
+          // 用户取消操作,不做任何处理
+        });
     },
-    mounted(){
-      this.queryForm.bucket_id=this.$route.query.id
+
+    deleteFolder(folderId) {
+      this.$confirm("确定要删除该文件夹吗?删除后无法恢复。", "警告", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          deleteType({ id: folderId })
+            .then((res) => {
+              if (res.status === 200) {
+                this.$message.success("删除文件夹成功");
+                this.typeList(); // 刷新文件夹列表
+              } else {
+                this.$message.error("删除文件夹失败");
+              }
+            })
+            .catch((error) => {
+              console.error("删除文件夹时出错:", error);
+              this.$message.error("删除文件夹时出错");
+            });
+        })
+        .catch(() => {
+          // 用户取消删除操作,不做任何处理
+        });
     },
-    methods: {
-      checkAuth(path) {
-        if (this.roleInfo.is_admin == 1) {
-          return true;
+
+    typeList() {
+      this.loading = true;
+      selectTypeList(this.typeForm)
+        .then((res) => {
+          if (res.status === 200) {
+            const folderList = res.data.dataList.map((folder) => ({
+              ...folder,
+              document_count: folder.file_count || 0, // 确保 document_count 存在
+            }));
+
+            this.folders = [
+              {
+                id: "0",
+                name: "All",
+                document_count: folderList.reduce(
+                  (sum, folder) => sum + folder.document_count,
+                  0
+                ),
+              },
+              ...folderList.filter((folder) => folder.id !== "0"),
+            ];
+            this.initializeTree();
+          } else {
+            this.$message.error("获取文件夹列表失败");
+          }
+        })
+        .catch((error) => {
+          console.error("获取文件夹列表时出错:", error);
+          this.$message.error("获取文件夹列表时出错");
+        })
+        .finally(() => {
+          this.loading = false;
+        });
+    },
+
+    initializeTree() {
+      this.$nextTick(() => {
+        if (this.$refs.folderTree) {
+          this.$refs.folderTree.setCurrentKey("0");
+          this.handleNodeClick(this.folders[0]);
+        }
+      });
+    },
+
+    createNewFolder() {
+      this.newFolderDialogVisible = true;
+    },
+    /* 新增 */
+    submitNewFolder() {
+      this.$refs.newFolderFormRef.validate((valid) => {
+        if (valid) {
+          this.newFolderForm.kb_id = this.$route.query.id;
+          insertType(this.newFolderForm).then((res) => {
+            if (res.status === 200) {
+              this.$message.success("创建文件夹成功");
+              this.newFolderDialogVisible = false;
+              this.$refs.newFolderFormRef.resetFields();
+              this.typeList(); // Refresh folder list
+            } else {
+              this.$message.error("创建文件夹失败");
+            }
+          });
         }
-        /* let auth=this.authList.filter(o=>o.type==999 && o.path==path);
-            if(auth.length>0){
-              return true;
-            } */
+      });
+    },
+
+    initializeTree() {
+      this.$refs.folderTree.setCurrentKey("0");
+      this.handleNodeClick(this.folders[0]); // Trigger initial search for "All"
+    },
+    checkAuth(path) {
+      if (this.roleInfo.is_admin === 1) {
         return true;
-      },
-  
-      setQuery(e) {
-        this.queryForm = e;
-      },
+      }
+      return this.authList.some(
+        (auth) => auth.type === 999 && auth.path === path
+      );
+    },
+
+    setQuery(e) {
+      this.queryForm = e;
+    },
+    handleNodeClick(data) {
+      this.queryForm.folder = data.id;
+      this.queryForm.page = 1; // Reset to first page when changing folder
+      this.search();
+    },
+
+    /*列表事件 */
+    /* 排序 */
+    handleSortChange({ prop, order }) {
+      this.sortColumn = prop;
+      this.sortOrder = order;
+      this.sortData();
+    },
+
+    sortData() {
+      const { sortColumn, sortOrder } = this;
+      if (!sortColumn || !sortOrder) {
+        this.search(); // Reset to original order
+        return;
+      }
+
+      this.dataList.sort((a, b) => {
+        let comparison = 0;
+        if (sortColumn === "name") {
+          comparison = a.name.localeCompare(b.name, "zh-CN");
+        } else if (sortColumn === "create_time") {
+          comparison = new Date(a.create_time) - new Date(b.create_time);
+        }
+        return sortOrder === "ascending" ? comparison : -comparison;
+      });
+    },
+    /* 批量解析 */
+    handleSelectionChange(val) {
+      this.selectedRows = val;
+    },
+
+    batchAnalysis() {
+      const selectedIds = this.selectedRows.map((row) => row.id);
+      if (selectedIds.length === 0) {
+        this.$message.warning("请选择要解析的文件");
+        return;
+      }
+
+      this.$confirm("确定要批量解析选中的文件吗?", "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.loading = true;
+          batchAnalysis({ ids: JSON.stringify(selectedIds) })
+            .then((res) => {
+              if (res.status === 200) {
+                this.$message.success("批量解析任务已提交");
+                this.updateAnalysisStatus(selectedIds);
+                this.clearSelection();
+              } else {
+                this.$message.error("批量解析请求失败");
+              }
+            })
+            .catch((error) => {
+              console.error("批量解析错误:", error);
+              this.$message.error("批量解析过程中出现错误");
+            })
+            .finally(() => {
+              this.loading = false;
+            });
+        })
+        .catch(() => {
+          // 取消操作
+        });
+    },
+
+    updateAnalysisStatus(ids) {
+      this.dataList.forEach((row) => {
+        if (ids.includes(row.id)) {
+          row.run = 1; // 设置状态为"解析中"
+          this.startAnalysisStatusChecker(row);
+        }
+      });
+    },
+
+    clearSelection() {
+      this.$refs.dataTable.clearSelection();
+      this.selectedRows = [];
     },
-  };
-  </script>
+
+    /* 跳转页面 */
+    getName(e) {
+      /* this.$router.push({
+        path: "/knowledge/category/infoList",
+        query: { id: e.id, type: e.type },
+      }); */
+      let _this = this;
+      let a = document.createElement("a");
+      a.href = `#/infoList?id=${e.id}&type=${e.type}`; // 使用 hash
+      a.target = "_blank";
+      a.click();
+    },
+    /* 解析 */
+    analysis(row) {
+      if (row.run === 1 || row.run === 5) return;
+
+      this.$set(row, "run", 1);
+
+      analysis({
+        document_id: row.id,
+        start_page: row.start_page || 0,
+        end_page: row.end_page || 1000,
+        max_tokens: row.token_num,
+      })
+        .then((res) => {
+          if (res.status === 200) {
+            this.startAnalysisStatusChecker(row);
+          } else {
+            this.$message.error("解析请求失败");
+            this.$set(row, "run", 0);
+          }
+        })
+        .catch((error) => {
+          console.error("解析错误:", error);
+          this.$message.error("解析过程中出现错误");
+          this.$set(row, "run", 0);
+        });
+    },
+
+    startAnalysisStatusChecker(row) {
+      // 如果已经有一个检查器在运行,先停止它
+      if (this.analysisStatusCheckers[row.id]) {
+        clearTimeout(this.analysisStatusCheckers[row.id]);
+      }
+
+      const checkStatus = () => {
+        getRunStatus({ document_id: row.id })
+          .then((res) => {
+            if (res.status === 200) {
+              const newStatus = Number(res.data.run);
+              this.$set(row, "run", newStatus);
+
+              if (newStatus === 3) {
+                this.$message.success("解析成功");
+                delete this.analysisStatusCheckers[row.id];
+              } else if (newStatus === 4) {
+                this.$message.error("解析失败");
+                delete this.analysisStatusCheckers[row.id];
+              } else if (newStatus === 1 || newStatus === 5) {
+                // 继续检查
+                this.analysisStatusCheckers[row.id] = setTimeout(
+                  checkStatus,
+                  5000 + Math.random() * 5000
+                );
+              }
+            } else {
+              this.$message.error("获取解析状态失败");
+              this.$set(row, "run", 0);
+              delete this.analysisStatusCheckers[row.id];
+            }
+          })
+          .catch((error) => {
+            console.error("获取解析状态错误:", error);
+            this.$message.error("获取解析状态时出现错误");
+            this.$set(row, "run", 0);
+            delete this.analysisStatusCheckers[row.id];
+          });
+      };
+
+      // 立即开始第一次检查
+      checkStatus();
+    },
+
+    handleAnalClose() {
+      this.anaDialogVisible = false;
+    },
+    /* 确认 */
+    submitAnal() {
+      this.dataList.map((el) => {
+        if (this.analForm.id == el.id) {
+          el.start_page = this.analForm.start_page;
+          el.end_page = this.analForm.end_page;
+          el.token_num = this.analForm.token_num;
+        }
+      });
+      this.anaDialogVisible = false;
+    },
+    /* 解析方法 */
+    Analytical(e) {
+      this.analForm.id = e.id;
+      this.analForm.token_num = e.token_num;
+      this.anaDialogVisible = true;
+    },
+    editWidth() {
+      if (this.allowDelete && this.allowEdit) {
+        return 200;
+      }
+      if (this.allowDelete || this.allowEdit) {
+        return 120;
+      }
+      return 100;
+    },
+
+    onFocusVal(e) {
+      let _this = this;
+      _this.currentDataId = e.target.dataset.id;
+    },
+
+    onChangeVal(e) {
+      let _this = this;
+      let par = {
+        id: _this.currentDataId,
+        val: e,
+      };
+    },
+
+    btnDelete(e) {
+      let _this = this;
+      let par = {
+        document_id: e,
+      };
+      _this
+        .$confirm("您是否确认删除该记录?", "提示", {
+          confirmButtonText: "确认",
+          cancelButtonText: "取消",
+          type: "warning",
+        })
+        .then((res) => {
+          deleteFile(par).then((res) => {
+            if (res.status !== 200) return;
+            _this.$message.success(res.data);
+            _this.search();
+          });
+        })
+        .catch(() => {});
+    },
+
+    searchData() {
+      let _this = this;
+      _this.dialogVisible = false;
+      _this.search();
+    },
+    /* 关闭 */
+    handleClose(done) {
+      done();
+    },
+    /* 修改文件名 */
+    btnEdit(e) {
+      this.wordForm.new_name = e.name;
+      this.wordForm.document_id = e.id;
+      this.dialogVisible = true;
+    },
+    /* 提交 */
+    submitBuck() {
+      this.$refs.wordRef.validate((valid) => {
+        if (valid) {
+          renameFile(this.wordForm).then((res) => {
+            if (res.status !== 200) return;
+            this.search();
+            this.dialogVisible = false;
+            this.wordForm = {
+              new_name: "",
+              document_id: "",
+            };
+          });
+        } else {
+          this.$message.error("请填写文档名称");
+        }
+      });
+    },
+
+    //下载
+    btnDown(e) {
+      // Set downloading flag for this specific row
+      this.$set(e, "downloading", true);
+
+      nameGetUrl({
+        document_id: e.id,
+      })
+        .then((res) => {
+          if (res.status !== 200) {
+            this.$message.error("获取下载链接失败");
+            return;
+          }
+
+          return fetch(res.data.url);
+        })
+        .then((response) => response.blob())
+        .then((blob) => {
+          const url = window.URL.createObjectURL(blob);
+          const link = document.createElement("a");
+          link.href = url;
+          link.download = e.name;
+          document.body.appendChild(link);
+          link.click();
+          setTimeout(() => {
+            document.body.removeChild(link);
+            window.URL.revokeObjectURL(url);
+          }, 100);
+        })
+        .catch((error) => {
+          console.error("下载出错:", error);
+          this.$message.error("下载失败,请稍后重试");
+        })
+        .finally(() => {
+          // Reset downloading flag when done
+          this.$set(e, "downloading", false);
+        });
+    },
+
+    handleClose() {
+      let _this = this;
+      _this.currentData = {};
+      _this.dialogVisible = false;
+    },
+
+    //搜索
+    search() {
+      let _this = this;
+      _this.loading = true;
+      getBucketContents(_this.queryForm)
+        .then((res) => {
+          if (res.status !== 200) return;
+          _this.dataList = res.data.documents.map((el) => ({
+            ...el,
+            token_num: 256,
+            start_page: _this.analForm.start_page,
+            end_page: _this.analForm.end_page,
+          }));
+          _this.pageTotal = res.data.pagination.total_count;
+          _this.recordCount = res.data.pagination.total_count;
+          _this.queryForm.pageSize = res.data.pagination.total_size;
+          _this.loading = false;
+
+          // Update file count for the selected folder
+          if (_this.queryForm.folder === "0") {
+            // If "All" is selected, update its document_count
+            _this.folders[0].document_count = _this.recordCount;
+          } else {
+            const selectedFolder = _this.folders.find(
+              (f) => f.id === _this.queryForm.folder
+            );
+            if (selectedFolder) {
+              selectedFolder.document_count = _this.recordCount;
+            }
+          }
+
+          // Initialize the analysis status for each row
+          _this.dataList.forEach((row) => {
+            if (row.run === 1) {
+              _this.startAnalysisStatusChecker(row);
+            }
+          });
+        })
+        .catch(() => {
+          _this.loading = false;
+        });
+    },
+    //修改分页
+    ChangePage(e) {
+      let _this = this;
+      _this.queryForm.page = e;
+      _this.search();
+    },
+    handleSizeChange(val) {
+      this.queryForm.pageSize = val;
+      this.queryForm.page = 1; // Reset to first page when changing page size
+      this.search();
+    },
+  },
+};
+</script>
     
-    <style lang="scss">
-  .project-search {
-    padding: 30px;
-    font-size: 12px;
+<style lang="scss" scoped>
+.project-search {
+  display: flex;
+  height: calc(100vh - 100px); // 假设顶部导航栏高度为60px
+  background-color: #f9fafb;
+  .custom-tree-node {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 8px 0;
+    position: relative;
+
+    .el-icon-folder {
+      color: #409eff;
+      margin-right: 8px;
+    }
+
+    .file-count {
+      color: #909399;
+      font-size: 12px;
+
+      padding: 2px 6px;
+      border-radius: 10px;
+      transition: opacity 0.3s;
+    }
+
+    .folder-actions {
+      position: absolute;
+      right: 0;
+      top: 50%;
+      transform: translateY(-50%);
+      display: none;
+
+      padding-left: 10px;
+
+      i {
+        font-size: 16px;
+        margin-left: 8px;
+        color: #606266;
+        cursor: pointer;
+
+        &:hover {
+          color: #409eff;
+        }
+      }
+    }
+
+    &:hover {
+      .file-count {
+        opacity: 0;
+      }
+
+      .folder-actions {
+        display: inline-block;
+      }
+    }
+  }
+
+  .el-tree-node__content:hover {
+    background-color: #f5f7fa;
+  }
+
+  .el-tree-node__content:hover {
+    background-color: #f5f7fa;
+  }
+
+  .file-categories {
+    flex: 0 0 280px;
+    padding: 20px;
+    background-color: #ffffff;
+    box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);
+    overflow-y: auto;
+
+    .el-button {
+      width: 100%;
+      margin-bottom: 20px;
+    }
+
+    .el-tree {
+      background-color: transparent;
+    }
+  }
+
+  .right {
+    flex: 1;
+    padding: 20px;
+    overflow-y: auto;
+  }
+
+  .custom-tree-node {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 8px 0;
+
+    .el-icon-folder {
+      color: #409eff;
+      margin-right: 8px;
+    }
+
+    .file-count {
+      color: #909399;
+      font-size: 12px;
+      background-color: #f0f2f5;
+      padding: 2px 6px;
+      border-radius: 10px;
+    }
   }
-  </style>
+}
+@import "./components/dataList.scss";
+</style>
     

Some files were not shown because too many files changed in this diff