knowledgeSet.vue 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365
  1. <template>
  2. <div class="project-search">
  3. <div class="file-categories">
  4. <el-button type="primary" @click="createNewFolder">
  5. <i class="el-icon-plus"></i> 新建目录
  6. </el-button>
  7. <p>{{ $route.query.name }}</p>
  8. <el-tree
  9. :data="folders"
  10. :props="defaultProps"
  11. @node-click="handleNodeClick"
  12. ref="folderTree"
  13. :default-expanded-keys="['0']"
  14. node-key="id"
  15. highlight-current
  16. >
  17. <span class="custom-tree-node" slot-scope="{ node, data }">
  18. <span class="tree_title">
  19. <i class="el-icon-folder"></i>
  20. <span class="folder-name" :title="node.label">{{
  21. node.label
  22. }}</span>
  23. </span>
  24. <span v-if="data.id !== '001'" :class="getFileCountClass(data.id)"
  25. >{{ data.document_count }} files</span
  26. >
  27. <span
  28. class="folder-actions"
  29. v-if="data.id !== 0 && data.id !== '001'"
  30. >
  31. <el-tooltip
  32. class="item"
  33. effect="dark"
  34. content="编辑"
  35. placement="top"
  36. >
  37. <i class="el-icon-edit" @click.stop="editFolder(data)"></i>
  38. </el-tooltip>
  39. <el-tooltip
  40. class="item"
  41. effect="dark"
  42. content="删除"
  43. placement="top"
  44. >
  45. <i class="el-icon-delete" @click.stop="deleteFolder(data.id)"></i>
  46. </el-tooltip>
  47. </span>
  48. </span>
  49. </el-tree>
  50. </div>
  51. <div class="right">
  52. <dataSearch
  53. @bindSetQuery="setQuery"
  54. @refreshFolders="typeList"
  55. ></dataSearch>
  56. <!-- <dataList
  57. :queryForm="queryForm"
  58. :allowEdit="checkAuth('/document/update')"
  59. :allowDelete="checkAuth('/document/delete')"
  60. ></dataList> -->
  61. <div
  62. class="data-list"
  63. v-loading="loading"
  64. element-loading-text="加载中..."
  65. >
  66. <div class="batch-actions" style="margin: 10px 0">
  67. <el-button
  68. type="primary"
  69. @click="batchAnalysis"
  70. :disabled="!selectedRows.length"
  71. >批量解析</el-button
  72. >
  73. <el-button
  74. type="primary"
  75. @click="batchModifyFolder"
  76. :disabled="!selectedRows.length"
  77. >批量修改目录</el-button
  78. >
  79. <el-button
  80. type="primary"
  81. @click="batchDelect"
  82. :disabled="!selectedRows.length"
  83. >批量删除</el-button
  84. >
  85. </div>
  86. <el-table
  87. ref="dataTable"
  88. :data="dataList"
  89. style="width: 100%; font-size: 12px"
  90. header-row-class-name="headerBg"
  91. empty-text="暂无数据"
  92. @selection-change="handleSelectionChange"
  93. @sort-change="handleSortChange"
  94. >
  95. <el-table-column type="selection" width="55"></el-table-column>
  96. <el-table-column prop="id" label="ID" align="center" width="80" />
  97. <el-table-column prop="name" label="文件名称" sortable width="350">
  98. <template #default="scope">
  99. <span>{{ scope.row.name }}</span>
  100. </template>
  101. </el-table-column>
  102. <el-table-column prop="type" label="文件类型" align="center" />
  103. <el-table-column prop="chunk_num" label="分块数" align="center" />
  104. <el-table-column
  105. prop="create_time"
  106. label="上传时间"
  107. align="center"
  108. sortable
  109. width="140"
  110. />
  111. <el-table-column prop="run" label="解析状态" align="center">
  112. <template #default="scope">
  113. <el-tag :type="getStatusType(scope.row.run)" size="small">
  114. {{ getStatusText(scope.row.run) }}
  115. </el-tag>
  116. </template>
  117. </el-table-column>
  118. <el-table-column
  119. label="操作"
  120. align="center"
  121. fixed="right"
  122. width="180"
  123. >
  124. <template #default="scope">
  125. <div class="operations-container">
  126. <el-tooltip
  127. class="item"
  128. effect="dark"
  129. content="预览"
  130. placement="top"
  131. >
  132. <el-button
  133. type="text"
  134. size="small"
  135. circle
  136. icon="el-icon-view"
  137. v-if="checkAuth('/document/update')"
  138. style="font-size: 15px"
  139. @click="getName(scope.row)"
  140. ></el-button>
  141. </el-tooltip>
  142. <el-tooltip
  143. class="item"
  144. effect="dark"
  145. content="修改文件名"
  146. placement="top"
  147. >
  148. <el-button
  149. type="text"
  150. size="small"
  151. circle
  152. icon="el-icon-edit"
  153. v-if="checkAuth('/document/update')"
  154. style="font-size: 15px"
  155. @click="btnEdit(scope.row)"
  156. ></el-button>
  157. </el-tooltip>
  158. <el-popover
  159. placement="bottom"
  160. trigger="click"
  161. popper-class="operations-popover"
  162. >
  163. <template #reference>
  164. <el-button
  165. type="text"
  166. size="small"
  167. circle
  168. class="operation-button"
  169. style="font-size: 15px; margin-left: 10px"
  170. icon="el-icon-more"
  171. ></el-button>
  172. </template>
  173. <div class="additional-operations">
  174. <el-tooltip
  175. class="item"
  176. effect="dark"
  177. :content="
  178. scope.row.run == 1 || scope.row.run == 5
  179. ? `当前状态:${getStatusText(scope.row.run)/* scope.row.progress */} 解析说明:${scope.row.progress_msg}`
  180. : '解析'
  181. "
  182. placement="top"
  183. >
  184. <el-button
  185. v-if="scope.row.run != 1 && scope.row.run != 5"
  186. type="text"
  187. icon="el-icon-caret-right"
  188. circle
  189. @click="analysis(scope.row)"
  190. style="font-size: 15px"
  191. >
  192. </el-button>
  193. <el-button
  194. v-else
  195. type="text"
  196. icon="el-icon-loading"
  197. circle
  198. :disabled="true"
  199. style="font-size: 15px"
  200. >
  201. </el-button>
  202. </el-tooltip>
  203. <el-tooltip
  204. class="item"
  205. effect="dark"
  206. content="解析方法"
  207. placement="top"
  208. >
  209. <el-button
  210. type="text"
  211. size="small"
  212. circle
  213. icon="el-icon-tickets"
  214. v-if="checkAuth('/document/update')"
  215. style="font-size: 15px"
  216. @click="Analytical(scope.row)"
  217. ></el-button>
  218. </el-tooltip>
  219. <el-tooltip
  220. class="item"
  221. effect="dark"
  222. content="下载"
  223. placement="top"
  224. >
  225. <el-button
  226. type="text"
  227. size="small"
  228. circle
  229. icon="el-icon-download"
  230. @click="btnDown(scope.row)"
  231. :loading="scope.row.downloading"
  232. style="font-size: 15px"
  233. ></el-button>
  234. </el-tooltip>
  235. <el-tooltip
  236. class="item"
  237. effect="dark"
  238. content="删除"
  239. placement="top"
  240. >
  241. <el-button
  242. type="text"
  243. size="small"
  244. circle
  245. icon="el-icon-delete"
  246. v-if="checkAuth('/document/delete')"
  247. @click="btnDelete(scope.row.id)"
  248. style="font-size: 15px"
  249. ></el-button>
  250. </el-tooltip>
  251. </div>
  252. </el-popover>
  253. </div>
  254. </template>
  255. </el-table-column>
  256. </el-table>
  257. <div class="page-info">
  258. <el-pagination
  259. :currentPage="queryForm.page"
  260. :page-size="queryForm.pageSize"
  261. :total="recordCount"
  262. :page-count="pageTotal"
  263. :page-sizes="[10, 20, 50, 100]"
  264. background
  265. layout="total,sizes, prev, pager, next"
  266. @current-change="ChangePage"
  267. @size-change="handleSizeChange"
  268. >
  269. </el-pagination>
  270. </div>
  271. <el-dialog
  272. title="修改名称"
  273. :visible.sync="dialogVisible"
  274. width="30%"
  275. :before-close="handleClose"
  276. >
  277. <el-form :model="wordForm" ref="wordRef" :rules="wordRules">
  278. <el-form-item label="文件名称:" prop="new_name">
  279. <el-input
  280. v-model="wordForm.new_name"
  281. placeholder="请输入文件名称"
  282. ></el-input>
  283. </el-form-item>
  284. </el-form>
  285. <span slot="footer" class="dialog-footer">
  286. <el-button @click="handleClose">取 消</el-button>
  287. <el-button type="primary" @click="submitBuck">确 定</el-button>
  288. </span>
  289. </el-dialog>
  290. <!-- 解析方法 -->
  291. <el-dialog
  292. title="解析方法"
  293. :visible.sync="anaDialogVisible"
  294. width="30%"
  295. :before-close="handleAnalClose"
  296. >
  297. <el-form
  298. :model="analForm"
  299. ref="analRef"
  300. label-position="top"
  301. :rules="analRules"
  302. >
  303. <el-form-item label="页码范围:" prop="new_name">
  304. <el-input-number
  305. v-model="analForm.start_page"
  306. controls-position="right"
  307. :min="1"
  308. ></el-input-number>
  309. <el-input-number
  310. v-model="analForm.end_page"
  311. controls-position="right"
  312. :min="1"
  313. style="margin-left: 10px"
  314. ></el-input-number>
  315. </el-form-item>
  316. <el-form-item label="块token数">
  317. <div class="block">
  318. <el-slider v-model="analForm.token_num" :max="2048" show-input>
  319. </el-slider>
  320. </div>
  321. </el-form-item>
  322. </el-form>
  323. <span slot="footer" class="dialog-footer">
  324. <el-button type="primary" @click="handleAnalClose">取 消</el-button>
  325. <el-button type="primary" @click="submitAnal">确 定</el-button>
  326. </span>
  327. </el-dialog>
  328. <el-dialog
  329. title="新建目录"
  330. :visible.sync="newFolderDialogVisible"
  331. width="30%"
  332. >
  333. <el-form
  334. :model="newFolderForm"
  335. :rules="newFolderRules"
  336. ref="newFolderFormRef"
  337. >
  338. <el-form-item label="文件夹名称" prop="name">
  339. <el-input v-model="newFolderForm.name"></el-input>
  340. </el-form-item>
  341. </el-form>
  342. <span slot="footer" class="dialog-footer">
  343. <el-button type="primary" @click="newFolderDialogVisible = false"
  344. >取 消</el-button
  345. >
  346. <el-button type="primary" @click="submitNewFolder">确 定</el-button>
  347. </span>
  348. </el-dialog>
  349. <!-- 批量修改目录 -->
  350. <el-dialog
  351. title="批量修改目录"
  352. :visible.sync="batchModifyFolderDialogVisible"
  353. width="30%"
  354. >
  355. <el-form
  356. :model="batchModifyFolderForm"
  357. :rules="batchModifyFolderRules"
  358. ref="batchModifyFolderFormRef"
  359. >
  360. <el-form-item label="选择目录" prop="folderId">
  361. <el-select
  362. v-model="batchModifyFolderForm.folderId"
  363. placeholder="请选择目录"
  364. >
  365. <el-option
  366. v-for="folder in folders.filter((f) => f.id !== 0)"
  367. :key="folder.id"
  368. :label="folder.name"
  369. :value="folder.id"
  370. >
  371. </el-option>
  372. </el-select>
  373. </el-form-item>
  374. </el-form>
  375. <span slot="footer" class="dialog-footer">
  376. <el-button
  377. type="primary"
  378. @click="batchModifyFolderDialogVisible = false"
  379. >取 消</el-button
  380. >
  381. <el-button type="primary" @click="submitBatchModifyFolder"
  382. >确 定</el-button
  383. >
  384. </span>
  385. </el-dialog>
  386. </div>
  387. </div>
  388. </div>
  389. </template>
  390. <script>
  391. import { mapGetters } from "vuex";
  392. import { dataSearch, dataList } from "./components";
  393. import {
  394. getBucketContents,
  395. deleteFile,
  396. nameGetUrl,
  397. renameFile,
  398. analysis,
  399. batchAnalysis,
  400. getRunStatus,
  401. insertType,
  402. updateType,
  403. selectTypeList,
  404. deleteType,
  405. Info,
  406. batchMove,
  407. selectType0,
  408. delDocumentList,
  409. getdocpro,
  410. analysisPro,
  411. resultTask,
  412. } from "@/api/knowledge";
  413. export default {
  414. components: {
  415. dataSearch,
  416. dataList,
  417. },
  418. computed: {
  419. ...mapGetters(["roleInfo", "authList"]),
  420. },
  421. data() {
  422. return {
  423. queryForm: {
  424. page: 1,
  425. pageSize: 10,
  426. bucket_id: "",
  427. doc_type_id: 0,
  428. },
  429. typeForm: {
  430. page: 1,
  431. pageSize: 9999,
  432. kb_id: "",
  433. },
  434. folders: [{ id: "001", name: "全部", document_count: 0 }],
  435. defaultProps: {
  436. children: "children",
  437. label: "name",
  438. },
  439. // dataList 组件的数据
  440. loading: false,
  441. dialogVisible: false,
  442. currentDataId: 0,
  443. recordCount: 0,
  444. pageTotal: 1,
  445. dataList: [],
  446. currentData: {},
  447. wordForm: {
  448. document_id: "",
  449. new_name: "",
  450. },
  451. wordRules: {
  452. new_name: [
  453. { required: true, message: "请输入文件名称", trigger: "blur" },
  454. ],
  455. },
  456. anaDialogVisible: false,
  457. analForm: {
  458. start_page: 1,
  459. end_page: 10000,
  460. token_num: 0,
  461. },
  462. analRules: {},
  463. selectedRows: [],
  464. sortColumn: "",
  465. sortOrder: "",
  466. analysisStatusCheckers: {},
  467. newFolderDialogVisible: false,
  468. newFolderForm: {
  469. name: "",
  470. kb_id: "",
  471. status: 5,
  472. },
  473. newFolderRules: {
  474. name: [
  475. { required: true, message: "请输入文件夹名称", trigger: "blur" },
  476. ],
  477. },
  478. /* 批量修改目录 */
  479. batchModifyFolderDialogVisible: false,
  480. batchModifyFolderForm: {
  481. folderId: null,
  482. },
  483. batchModifyFolderRules: {
  484. folderId: [
  485. { required: true, message: "请选择目标目录", trigger: "change" },
  486. ],
  487. },
  488. };
  489. },
  490. mounted() {
  491. this.queryForm.bucket_id = this.$route.query.id;
  492. this.typeForm.kb_id = this.$route.query.id;
  493. this.$nextTick(() => {
  494. this.initializeTree();
  495. });
  496. this.typeList();
  497. this.search();
  498. },
  499. beforeDestroy() {
  500. Object.values(this.analysisStatusCheckers).forEach(clearTimeout);
  501. },
  502. methods: {
  503. /* 操作栏 */
  504. handleCommand(command, row) {
  505. switch (command) {
  506. case "analysis":
  507. this.analysis(row);
  508. break;
  509. case "analyticalMethod":
  510. this.Analytical(row);
  511. break;
  512. case "download":
  513. this.btnDown(row);
  514. break;
  515. case "delete":
  516. this.btnDelete(row.id);
  517. break;
  518. }
  519. },
  520. /* 状态展示 */
  521. getStatusType(status) {
  522. const statusMap = {
  523. 0: "info", // 未解析
  524. 1: "warning", // 解析中
  525. 3: "success", // 成功
  526. 4: "danger", // 失败
  527. 5: "warning", // 待处理
  528. 6: "danger", // 文件异常
  529. };
  530. return statusMap[status] || "info";
  531. },
  532. getStatusText(status) {
  533. const statusMap = {
  534. 0: "未解析",
  535. 1: "解析中",
  536. 3: "成功",
  537. 4: "失败",
  538. 5: "待处理",
  539. 6: "文件异常",
  540. };
  541. return statusMap[status] || "未知状态";
  542. },
  543. /* 判断类名 */
  544. getFileCountClass(id) {
  545. const isSpecialFolder = id === 0 || id === "001";
  546. console.log(
  547. `ID: ${id}, Class: ${isSpecialFolder ? "file-counts" : "file-count"}`
  548. );
  549. return isSpecialFolder ? "file-counts" : "file-count";
  550. },
  551. /* 批量修改目录 */
  552. batchModifyFolder() {
  553. if (this.selectedRows.length === 0) {
  554. this.$message.warning("请选择要修改目录的文件");
  555. return;
  556. }
  557. this.batchModifyFolderDialogVisible = true;
  558. },
  559. submitBatchModifyFolder() {
  560. this.$refs.batchModifyFolderFormRef.validate((valid) => {
  561. if (valid) {
  562. const selectedIds = this.selectedRows.map((row) => row.id);
  563. console.log(selectedIds);
  564. batchMove({
  565. ids: JSON.stringify(selectedIds),
  566. doc_type_id: this.batchModifyFolderForm.folderId,
  567. })
  568. .then((res) => {
  569. if (res.status === 200) {
  570. this.$message.success("批量修改目录成功");
  571. this.batchModifyFolderDialogVisible = false;
  572. this.search(); // Refresh the document list
  573. this.typeList(); // Refresh the folder list
  574. this.clearSelection();
  575. } else {
  576. this.$message.error("批量修改目录失败");
  577. }
  578. })
  579. .catch((error) => {
  580. console.error("批量修改目录时出错:", error);
  581. this.$message.error("批量修改目录时出错");
  582. });
  583. }
  584. });
  585. },
  586. editFolder(folder) {
  587. this.$prompt("请输入新的文件夹名称", "编辑文件夹", {
  588. confirmButtonText: "确定",
  589. cancelButtonText: "取消",
  590. inputValue: folder.name,
  591. inputValidator: (value) => {
  592. if (!value) {
  593. return "文件夹名称不能为空";
  594. }
  595. return true;
  596. },
  597. })
  598. .then(({ value }) => {
  599. if (value !== folder.name) {
  600. const updatedFolder = { ...folder, name: value };
  601. updateType(updatedFolder)
  602. .then((res) => {
  603. if (res.status === 200) {
  604. this.$message.success("更新文件夹成功");
  605. this.typeList(); // 刷新文件夹列表
  606. } else {
  607. this.$message.error("更新文件夹失败");
  608. }
  609. })
  610. .catch((error) => {
  611. console.error("更新文件夹时出错:", error);
  612. this.$message.error("更新文件夹时出错");
  613. });
  614. }
  615. })
  616. .catch(() => {
  617. // 用户取消操作,不做任何处理
  618. });
  619. },
  620. deleteFolder(folderId) {
  621. this.$confirm("确定要删除该文件夹吗?删除后无法恢复。", "警告", {
  622. confirmButtonText: "确定",
  623. cancelButtonText: "取消",
  624. type: "warning",
  625. })
  626. .then(() => {
  627. deleteType({ id: folderId })
  628. .then((res) => {
  629. if (res.status === 200) {
  630. this.$message.success("删除文件夹成功");
  631. this.typeList(); // 刷新文件夹列表
  632. } else {
  633. this.$message.error("删除文件夹失败");
  634. }
  635. })
  636. .catch((error) => {
  637. console.error("删除文件夹时出错:", error);
  638. this.$message.error("删除文件夹时出错");
  639. });
  640. })
  641. .catch(() => {
  642. // 用户取消删除操作,不做任何处理
  643. });
  644. },
  645. /* 目录初始化 */
  646. typeList() {
  647. this.loading = true;
  648. Promise.all([
  649. selectTypeList(this.typeForm),
  650. selectType0({ kb_id: this.typeForm.kb_id, type_id: 0 }),
  651. ])
  652. .then(([typeListRes, type0Res]) => {
  653. if (typeListRes.status === 200 && type0Res.status === 200) {
  654. const folderList = typeListRes.data.dataList.map((folder) => ({
  655. ...folder,
  656. id: folder.id === 0 ? "001" : folder.id.toString(),
  657. document_count: folder.document_count || 0,
  658. }));
  659. // Calculate total documents
  660. const totalDocuments = folderList.reduce(
  661. (sum, folder) => sum + folder.document_count,
  662. 0
  663. );
  664. // Get the count of "其他" documents from selectType0 response
  665. const otherFolderCount = type0Res.data || 0;
  666. this.folders = [
  667. {
  668. id: "001",
  669. name: "全部",
  670. document_count: totalDocuments + otherFolderCount,
  671. },
  672. ...folderList.filter(
  673. (folder) => folder.id !== "001" && folder.id !== 0
  674. ),
  675. {
  676. id: 0,
  677. name: "其他",
  678. document_count: otherFolderCount,
  679. },
  680. ];
  681. this.$nextTick(() => {
  682. this.initializeTree();
  683. });
  684. } else {
  685. this.$message.error("获取文件夹列表失败");
  686. }
  687. })
  688. .catch((error) => {
  689. console.error("获取文件夹列表时出错:", error);
  690. this.$message.error("获取文件夹列表时出错");
  691. })
  692. .finally(() => {
  693. this.loading = false;
  694. });
  695. },
  696. initializeTree() {
  697. this.$nextTick(() => {
  698. if (this.$refs.folderTree) {
  699. this.$refs.folderTree.setCurrentKey(0);
  700. this.handleNodeClick(this.folders[0]);
  701. }
  702. });
  703. },
  704. createNewFolder() {
  705. this.newFolderDialogVisible = true;
  706. },
  707. /* 新增 */
  708. submitNewFolder() {
  709. this.$refs.newFolderFormRef.validate((valid) => {
  710. if (valid) {
  711. this.newFolderForm.kb_id = this.$route.query.id;
  712. insertType(this.newFolderForm).then((res) => {
  713. if (res.status === 200) {
  714. this.$message.success("创建文件夹成功");
  715. this.newFolderDialogVisible = false;
  716. this.$refs.newFolderFormRef.resetFields();
  717. this.typeList(); // Refresh folder list
  718. } else {
  719. this.$message.error("创建文件夹失败");
  720. }
  721. });
  722. }
  723. });
  724. },
  725. initializeTree() {
  726. this.$nextTick(() => {
  727. if (this.$refs.folderTree) {
  728. this.$refs.folderTree.setCurrentKey("001");
  729. this.handleNodeClick(this.folders[0]);
  730. }
  731. });
  732. },
  733. checkAuth(path) {
  734. if (this.roleInfo.is_admin === 1) {
  735. return true;
  736. }
  737. return this.authList.some(
  738. (auth) => auth.type === 999 && auth.path === path
  739. );
  740. },
  741. setQuery(e) {
  742. this.queryForm = { ...this.queryForm, ...e };
  743. this.search();
  744. },
  745. handleNodeClick(data) {
  746. this.queryForm.doc_type_id = data.id === "001" ? "" : data.id;
  747. this.queryForm.page = 1; // Reset to first page when changing folder
  748. this.search();
  749. },
  750. /*列表事件 */
  751. /* 排序 */
  752. handleSortChange({ prop, order }) {
  753. this.sortColumn = prop;
  754. this.sortOrder = order;
  755. this.sortData();
  756. },
  757. sortData() {
  758. const { sortColumn, sortOrder } = this;
  759. if (!sortColumn || !sortOrder) {
  760. this.search(); // Reset to original order
  761. return;
  762. }
  763. this.dataList.sort((a, b) => {
  764. let comparison = 0;
  765. if (sortColumn === "name") {
  766. comparison = a.name.localeCompare(b.name, "zh-CN");
  767. } else if (sortColumn === "create_time") {
  768. comparison = new Date(a.create_time) - new Date(b.create_time);
  769. }
  770. return sortOrder === "ascending" ? comparison : -comparison;
  771. });
  772. },
  773. /* 批量解析 */
  774. handleSelectionChange(val) {
  775. this.selectedRows = val;
  776. },
  777. batchAnalysis() {
  778. const selectedIds = this.selectedRows.map((row) => row.id);
  779. if (selectedIds.length === 0) {
  780. this.$message.warning("请选择要解析的文件");
  781. return;
  782. }
  783. this.$confirm("确定要批量解析选中的文件吗?", "提示", {
  784. confirmButtonText: "确定",
  785. cancelButtonText: "取消",
  786. type: "warning",
  787. })
  788. .then(() => {
  789. this.loading = true;
  790. analysisPro({
  791. document_ids: JSON.stringify(selectedIds),
  792. }) /* { ids: JSON.stringify(selectedIds) } */
  793. .then((res) => {
  794. if (res.status === 200) {
  795. this.$message.success("批量解析任务已提交");
  796. this.updateAnalysisStatus(selectedIds);
  797. this.clearSelection();
  798. } else {
  799. this.$message.error("批量解析请求失败");
  800. }
  801. })
  802. .catch((error) => {
  803. console.error("批量解析错误:", error);
  804. this.$message.error("批量解析过程中出现错误");
  805. })
  806. .finally(() => {
  807. this.loading = false;
  808. });
  809. })
  810. .catch(() => {
  811. // 取消操作
  812. });
  813. },
  814. /* 批量删除 */
  815. batchDelect() {
  816. const selectedIds = this.selectedRows.map((row) => row.id);
  817. if (selectedIds.length === 0) {
  818. this.$message.warning("请选择要删除的文件");
  819. return;
  820. }
  821. this.$confirm("确定要批量删除选中的文件吗?", "提示", {
  822. confirmButtonText: "确定",
  823. cancelButtonText: "取消",
  824. type: "warning",
  825. })
  826. .then(() => {
  827. this.loading = true;
  828. delDocumentList({ document_ids: JSON.stringify(selectedIds) })
  829. .then((res) => {
  830. if (res.status === 200) {
  831. this.$message.success("批量删除已完成");
  832. this.search();
  833. } else {
  834. this.$message.error("批量删除请求失败");
  835. }
  836. })
  837. .catch((error) => {
  838. /* console.error("批量删除错误:", error);
  839. this.$message.error("批量删除过程中出现错误"); */
  840. })
  841. .finally(() => {
  842. this.loading = false;
  843. });
  844. })
  845. .catch(() => {
  846. // 取消操作
  847. });
  848. },
  849. updateAnalysisStatus(ids) {
  850. this.dataList.forEach((row) => {
  851. if (ids.includes(row.id)) {
  852. row.run = 1; // 设置状态为"解析中"
  853. this.startAnalysisStatusChecker(row);
  854. }
  855. });
  856. },
  857. clearSelection() {
  858. this.$refs.dataTable.clearSelection();
  859. this.selectedRows = [];
  860. },
  861. /* 跳转页面 */
  862. getName(e) {
  863. /* this.$router.push({
  864. path: "/knowledge/category/infoList",
  865. query: { id: e.id, type: e.type },
  866. }); */
  867. let _this = this;
  868. let a = document.createElement("a");
  869. a.href = `#/infoList?id=${e.id}&type=${e.type.toLowerCase()}`; // 使用 hash
  870. a.target = "_blank";
  871. a.click();
  872. },
  873. /* 解析 */
  874. analysis(row) {
  875. if (row.run == 1 || row.run == 5) return;
  876. // Set the status to "解析中" (1) immediately
  877. this.$set(row, "run", 1);
  878. const analysisConfig = {
  879. document_ids: JSON.stringify([row.id]),
  880. start_page: row.start_page ?? 0, // 使用空值合并运算符
  881. end_page: row.end_page ?? 1000, // 使用空值合并运算符
  882. max_tokens: row.token_num,
  883. };
  884. analysisPro(analysisConfig)
  885. .then((res) => {
  886. if (res.status === 200) {
  887. // Start checking the status immediately after successful API call
  888. this.startAnalysisStatusChecker(row);
  889. } else {
  890. this.$message.error("解析请求失败");
  891. this.$set(row, "run", 0);
  892. }
  893. })
  894. .catch((error) => {
  895. console.error("解析错误:", error);
  896. this.$message.error("解析过程中出现错误");
  897. this.$set(row, "run", 0);
  898. });
  899. },
  900. startAnalysisStatusChecker(row) {
  901. // Clear any existing checker for this row
  902. if (this.analysisStatusCheckers[row.id]) {
  903. clearTimeout(this.analysisStatusCheckers[row.id]);
  904. }
  905. /* 获取token数 */
  906. /* const checkStatus = () => {
  907. getdocpro({ document_id: row.id })
  908. .then((res) => {
  909. if (res.status === 200) {
  910. const newStatus = Number(res.data.status);
  911. const newProgress = Number(res.data.total_tasks);
  912. this.$set(row, "run", newStatus);
  913. this.$set(row, "progress", newProgress);
  914. if (newStatus === 3) {
  915. this.$message.success("解析成功");
  916. delete this.analysisStatusCheckers[row.id];
  917. // 在这里添加刷新列表的逻辑
  918. this.refreshCurrentList();
  919. } else if (newStatus === 4) {
  920. this.$message.error("解析失败");
  921. delete this.analysisStatusCheckers[row.id];
  922. } else if (newStatus === 1 || newStatus === 5) {
  923. // Continue checking
  924. this.analysisStatusCheckers[row.id] = setTimeout(
  925. checkStatus,
  926. 15000
  927. );
  928. }
  929. } else {
  930. this.$message.error("获取解析状态失败");
  931. delete this.analysisStatusCheckers[row.id];
  932. }
  933. })
  934. .catch((error) => {
  935. console.error("获取解析状态错误:", error);
  936. this.$message.error("获取解析状态时出现错误");
  937. delete this.analysisStatusCheckers[row.id];
  938. });
  939. }; */
  940. /* 监控任务 */
  941. const checkStatus = () => {
  942. resultTask(row.id)
  943. .then((res) => {
  944. if (res.status === 200) {
  945. const newStatus = Number(res.data.document_status);
  946. const newProgress = res.data.progress_msg
  947. this.$set(row, "run", newStatus);
  948. this.$set(row, "progress_msg", newProgress);
  949. if (newStatus === 3) {
  950. this.$message.success("解析成功");
  951. delete this.analysisStatusCheckers[row.id];
  952. // 在这里添加刷新列表的逻辑
  953. this.refreshCurrentList();
  954. } else if (newStatus === 4) {
  955. this.$message.error("解析失败");
  956. delete this.analysisStatusCheckers[row.id];
  957. } else if (newStatus === 1 || newStatus === 5) {
  958. // Continue checking
  959. this.analysisStatusCheckers[row.id] = setTimeout(
  960. checkStatus,
  961. 15000
  962. );
  963. }
  964. } else {
  965. this.$message.error("获取解析状态失败");
  966. delete this.analysisStatusCheckers[row.id];
  967. }
  968. })
  969. .catch((error) => {
  970. console.error("获取解析状态错误:", error);
  971. this.$message.error("获取解析状态时出现错误");
  972. delete this.analysisStatusCheckers[row.id];
  973. });
  974. };
  975. // Start checking immediately
  976. checkStatus();
  977. },
  978. // 修改 refreshCurrentList 方法
  979. refreshCurrentList() {
  980. // 保存当前页码和每页显示数量
  981. const currentPage = this.queryForm.page;
  982. const currentPageSize = this.queryForm.pageSize;
  983. // 重新获取数据
  984. return this.search()
  985. .then(() => {
  986. // 恢复之前的页码和每页显示数量
  987. this.queryForm.page = currentPage;
  988. this.queryForm.pageSize = currentPageSize;
  989. })
  990. .catch((error) => {
  991. console.error("刷新列表时出错:", error);
  992. this.$message.error("刷新列表失败,请稍后重试");
  993. });
  994. },
  995. handleAnalClose() {
  996. this.anaDialogVisible = false;
  997. },
  998. /* 确认 */
  999. submitAnal() {
  1000. this.dataList.map((el) => {
  1001. if (this.analForm.id == el.id) {
  1002. el.start_page = this.analForm.start_page;
  1003. el.end_page = this.analForm.end_page;
  1004. el.token_num = this.analForm.token_num;
  1005. }
  1006. });
  1007. this.anaDialogVisible = false;
  1008. },
  1009. /* 解析方法 */
  1010. Analytical(e) {
  1011. this.analForm.id = e.id;
  1012. this.analForm.token_num = e.token_num;
  1013. this.anaDialogVisible = true;
  1014. },
  1015. editWidth() {
  1016. if (this.allowDelete && this.allowEdit) {
  1017. return 200;
  1018. }
  1019. if (this.allowDelete || this.allowEdit) {
  1020. return 120;
  1021. }
  1022. return 100;
  1023. },
  1024. onFocusVal(e) {
  1025. let _this = this;
  1026. _this.currentDataId = e.target.dataset.id;
  1027. },
  1028. onChangeVal(e) {
  1029. let _this = this;
  1030. let par = {
  1031. id: _this.currentDataId,
  1032. val: e,
  1033. };
  1034. },
  1035. btnDelete(e) {
  1036. let _this = this;
  1037. let par = {
  1038. document_id: e,
  1039. };
  1040. _this
  1041. .$confirm("您是否确认删除该记录?", "提示", {
  1042. confirmButtonText: "确认",
  1043. cancelButtonText: "取消",
  1044. type: "warning",
  1045. })
  1046. .then((res) => {
  1047. deleteFile(par).then((res) => {
  1048. if (res.status !== 200) return;
  1049. _this.$message.success(res.data);
  1050. _this.search();
  1051. });
  1052. })
  1053. .catch(() => {});
  1054. },
  1055. searchData() {
  1056. let _this = this;
  1057. _this.dialogVisible = false;
  1058. _this.search();
  1059. },
  1060. /* 关闭 */
  1061. handleClose(done) {
  1062. done();
  1063. },
  1064. /* 修改文件名 */
  1065. btnEdit(e) {
  1066. this.wordForm.new_name = e.name;
  1067. this.wordForm.document_id = e.id;
  1068. this.dialogVisible = true;
  1069. },
  1070. /* 提交 */
  1071. submitBuck() {
  1072. this.$refs.wordRef.validate((valid) => {
  1073. if (valid) {
  1074. renameFile(this.wordForm).then((res) => {
  1075. if (res.status !== 200) return;
  1076. this.search();
  1077. this.dialogVisible = false;
  1078. this.wordForm = {
  1079. new_name: "",
  1080. document_id: "",
  1081. };
  1082. });
  1083. } else {
  1084. this.$message.error("请填写文档名称");
  1085. }
  1086. });
  1087. },
  1088. //下载
  1089. btnDown(e) {
  1090. // Set downloading flag for this specific row
  1091. this.$set(e, "downloading", true);
  1092. nameGetUrl({
  1093. document_id: e.id,
  1094. })
  1095. .then((res) => {
  1096. if (res.status !== 200) {
  1097. this.$message.error("获取下载链接失败");
  1098. return;
  1099. }
  1100. return fetch(res.data.url);
  1101. })
  1102. .then((response) => response.blob())
  1103. .then((blob) => {
  1104. const url = window.URL.createObjectURL(blob);
  1105. const link = document.createElement("a");
  1106. link.href = url;
  1107. link.download = e.name;
  1108. document.body.appendChild(link);
  1109. link.click();
  1110. setTimeout(() => {
  1111. document.body.removeChild(link);
  1112. window.URL.revokeObjectURL(url);
  1113. }, 100);
  1114. })
  1115. .catch((error) => {
  1116. console.error("下载出错:", error);
  1117. this.$message.error("下载失败,请稍后重试");
  1118. })
  1119. .finally(() => {
  1120. // Reset downloading flag when done
  1121. this.$set(e, "downloading", false);
  1122. });
  1123. },
  1124. handleClose() {
  1125. let _this = this;
  1126. _this.currentData = {};
  1127. _this.dialogVisible = false;
  1128. },
  1129. //搜索
  1130. search() {
  1131. let _this = this;
  1132. _this.loading = true;
  1133. return new Promise((resolve, reject) => {
  1134. getBucketContents(_this.queryForm)
  1135. .then((res) => {
  1136. if (res.status !== 200) {
  1137. reject(new Error("Invalid response status"));
  1138. return;
  1139. }
  1140. _this.dataList = res.data.documents.map((el) => ({
  1141. ...el,
  1142. token_num: 256,
  1143. start_page: _this.analForm.start_page,
  1144. end_page: _this.analForm.end_page,
  1145. }));
  1146. _this.pageTotal = res.data.pagination.total_count;
  1147. _this.recordCount = res.data.pagination.total_count;
  1148. _this.queryForm.pageSize = res.data.pagination.total_size;
  1149. _this.loading = false;
  1150. // Initialize the analysis status for each row
  1151. _this.dataList.forEach((row) => {
  1152. if (row.run == 1 || row.run == 5) {
  1153. _this.startAnalysisStatusChecker(row);
  1154. }
  1155. });
  1156. resolve();
  1157. })
  1158. .catch((error) => {
  1159. _this.loading = false;
  1160. reject(error);
  1161. });
  1162. });
  1163. },
  1164. //修改分页
  1165. ChangePage(e) {
  1166. let _this = this;
  1167. _this.queryForm.page = e;
  1168. _this.search();
  1169. },
  1170. handleSizeChange(val) {
  1171. this.queryForm.pageSize = val;
  1172. this.queryForm.page = 1; // Reset to first page when changing page size
  1173. this.search();
  1174. },
  1175. },
  1176. };
  1177. </script>
  1178. <style lang="scss" scoped>
  1179. .project-search {
  1180. display: flex;
  1181. height: calc(100vh - 100px); // 假设顶部导航栏高度为60px
  1182. background-color: #f9fafb;
  1183. .custom-tree-node {
  1184. width: 100%;
  1185. display: flex;
  1186. align-items: center;
  1187. justify-content: space-between;
  1188. padding: 8px 0;
  1189. position: relative;
  1190. .el-icon-folder {
  1191. color: #409eff;
  1192. margin-right: 8px;
  1193. }
  1194. .file-count {
  1195. color: #909399;
  1196. font-size: 12px;
  1197. padding: 2px 6px;
  1198. border-radius: 10px;
  1199. transition: opacity 0.3s;
  1200. }
  1201. .file-counts {
  1202. color: #909399;
  1203. font-size: 12px;
  1204. padding: 2px 6px;
  1205. border-radius: 10px;
  1206. transition: opacity 0.3s;
  1207. }
  1208. .folder-actions {
  1209. position: absolute;
  1210. right: 0;
  1211. top: 50%;
  1212. transform: translateY(-50%);
  1213. display: none;
  1214. padding-left: 10px;
  1215. i {
  1216. font-size: 16px;
  1217. margin-left: 8px;
  1218. color: #606266;
  1219. cursor: pointer;
  1220. &:hover {
  1221. color: #409eff;
  1222. }
  1223. }
  1224. }
  1225. &:hover {
  1226. .file-count {
  1227. opacity: 0;
  1228. }
  1229. .folder-actions {
  1230. display: inline-block;
  1231. }
  1232. }
  1233. }
  1234. .el-tree-node__content:hover {
  1235. background-color: #f5f7fa;
  1236. }
  1237. .el-tree-node__content:hover {
  1238. background-color: #f5f7fa;
  1239. }
  1240. .file-categories {
  1241. flex: 0 0 280px;
  1242. padding: 20px;
  1243. background-color: #ffffff;
  1244. box-shadow: 2px 0 10px rgba(0, 0, 0, 0.05);
  1245. overflow-y: auto;
  1246. .el-button {
  1247. width: 100%;
  1248. margin-bottom: 20px;
  1249. }
  1250. .el-tree {
  1251. background-color: transparent;
  1252. }
  1253. }
  1254. .right {
  1255. flex: 1;
  1256. padding: 20px;
  1257. overflow-y: auto;
  1258. }
  1259. .custom-tree-node {
  1260. font-size: 14px;
  1261. width: 100%;
  1262. display: flex;
  1263. align-items: center;
  1264. justify-content: space-between;
  1265. padding: 8px 0;
  1266. .el-icon-folder {
  1267. color: #409eff;
  1268. margin-right: 8px;
  1269. }
  1270. .file-count {
  1271. color: #909399;
  1272. font-size: 12px;
  1273. background-color: #f0f2f5;
  1274. padding: 2px 6px;
  1275. border-radius: 10px;
  1276. }
  1277. .tree_title {
  1278. display: flex;
  1279. align-items: center;
  1280. width: 140px;
  1281. .folder-name {
  1282. white-space: nowrap;
  1283. overflow: hidden;
  1284. text-overflow: ellipsis;
  1285. max-width: calc(100% - 24px); // 减去图标的宽度
  1286. }
  1287. }
  1288. }
  1289. }
  1290. .el-tree {
  1291. margin-left: 10px;
  1292. }
  1293. ::v-deep .el-tree-node__expand-icon {
  1294. display: none;
  1295. }
  1296. @import "./components/dataList.scss";
  1297. </style>