tool.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731
  1. <template>
  2. <a-button type="text" @click="handleClick" size="small">
  3. <template #icon>
  4. <icon-tool />
  5. </template>
  6. </a-button>
  7. <a-modal
  8. v-model:visible="visible"
  9. title=" "
  10. @before-open="handleOpened"
  11. @cancel="handleCancel"
  12. :footer="false"
  13. title-align="start"
  14. width="900px"
  15. >
  16. <a-tabs default-active-key="1">
  17. <a-tab-pane key="1">
  18. <template #title>
  19. <icon-calendar />
  20. 解析方法
  21. </template>
  22. <div style="width: 100%">
  23. <div style="display: flex; align-items: center">
  24. <div> 解析方法:</div>
  25. <div style="margin-left: 10px">
  26. <a-select v-model="form.parser_id" placeholder="请选择">
  27. <a-option
  28. v-for="item in parser_ids"
  29. :key="item.value"
  30. :label="item.name"
  31. :value="item.value"
  32. ></a-option>
  33. </a-select>
  34. </div>
  35. </div>
  36. <div class="parser">
  37. <a-form
  38. ref="formRef"
  39. :rules="rules"
  40. :model="form"
  41. auto-label-width
  42. @submit="handleSubmit"
  43. >
  44. <a-divider
  45. style="margin-top: 10px"
  46. v-if="form.parser_id === 'naive'"
  47. />
  48. <a-form-item
  49. field="slider"
  50. label="块Token数"
  51. v-if="form.parser_id === 'naive'"
  52. >
  53. <a-slider
  54. v-model="form.chunk_token_num"
  55. :min="0"
  56. :max="2048"
  57. show-input
  58. />
  59. </a-form-item>
  60. <a-divider
  61. style="margin-top: 10px"
  62. v-if="
  63. form.parser_id === 'naive' ||
  64. form.parser_id === 'qa' ||
  65. form.parser_id === 'manual' ||
  66. form.parser_id === 'laws' ||
  67. form.parser_id === 'book'
  68. "
  69. />
  70. <a-form-item
  71. field="raptor"
  72. label="使用召回增强RAPTOR策略"
  73. v-if="
  74. form.parser_id === 'naive' ||
  75. form.parser_id === 'qa' ||
  76. form.parser_id === 'manual' ||
  77. form.parser_id === 'laws' ||
  78. form.parser_id === 'book'
  79. "
  80. >
  81. <a-space direction="vertical" size="large">
  82. <a-switch
  83. v-model="form.use_raptor"
  84. @change="onChangeRAPTOR"
  85. />
  86. </a-space>
  87. </a-form-item>
  88. <div v-if="form.use_raptor">
  89. <a-form-item field="prompt" label="提示词">
  90. <a-textarea
  91. v-model="form.prompt"
  92. style="
  93. height: 10rem;
  94. border: 1px solid var(--color-fill-3);
  95. border-radius: 4px;
  96. "
  97. placeholder="请填写提示词"
  98. />
  99. </a-form-item>
  100. <a-form-item field="slider" label="最大token数">
  101. <a-slider
  102. v-model="form.max_token"
  103. :min="0"
  104. :max="2048"
  105. show-input
  106. />
  107. </a-form-item>
  108. <a-form-item field="slider" label="阈值">
  109. <a-slider
  110. v-model="form.threshold"
  111. :step="0.01"
  112. :min="0"
  113. :max="1"
  114. show-input
  115. />
  116. </a-form-item>
  117. <a-form-item field="slider" label="最大聚类数">
  118. <a-slider
  119. v-model="form.max_cluster"
  120. :min="1"
  121. :max="1024"
  122. show-input
  123. />
  124. </a-form-item>
  125. <a-form-item
  126. field="slider"
  127. label="随机种子"
  128. v-model="form.random_seed"
  129. :rules="[
  130. { type: 'number', min: 5, message: 'slider is min than 5' },
  131. ]"
  132. >
  133. <a-input-number
  134. v-model="form.random_seed"
  135. :style="{
  136. width: '300px',
  137. marginRight: '1rem',
  138. borderRadius: '4px',
  139. }"
  140. placeholder="请输入"
  141. class="input-demo"
  142. :min="10"
  143. :max="100"
  144. />
  145. <a-button type="primary" @click="randomNumber">
  146. <icon-plus />
  147. </a-button>
  148. </a-form-item>
  149. </div>
  150. <a-form-item>
  151. <div style="width: 100%; text-align: right">
  152. <a-button @click="visible = false">取消</a-button>
  153. <a-button
  154. style="margin-left: 10px"
  155. type="primary"
  156. html-type="submit"
  157. >确定
  158. </a-button>
  159. </div>
  160. </a-form-item>
  161. </a-form>
  162. </div>
  163. </div>
  164. </a-tab-pane>
  165. <a-tab-pane key="2">
  166. <template #title>
  167. <icon-clock-circle />
  168. 创建解析块
  169. </template>
  170. <div class="details">
  171. <div class="details-header">
  172. <div
  173. style="
  174. display: flex;
  175. align-items: center;
  176. justify-content: flex-end;
  177. "
  178. >
  179. <a-radio-group
  180. v-model="radio"
  181. type="button"
  182. style="margin-right: 10px"
  183. @change="onRadioChange"
  184. >
  185. <a-radio value="1">全文</a-radio>
  186. <a-radio value="2">省略</a-radio>
  187. </a-radio-group>
  188. <a-popover position="bottom">
  189. <a-button
  190. type="outline"
  191. class="button"
  192. style="margin-right: 10px"
  193. >批量
  194. <icon-down style="margin-left: 4px" />
  195. </a-button>
  196. <template #content>
  197. <a-space direction="vertical">
  198. <a-checkbox
  199. :model-value="checkedAll"
  200. :indeterminate="indeterminate"
  201. @change="handleChangeAll"
  202. style="margin-left: 10px"
  203. >
  204. 选择所有
  205. </a-checkbox>
  206. </a-space>
  207. <a-divider style="margin: 10px 0" />
  208. <p>
  209. <a-button
  210. type="text"
  211. class="button"
  212. style="color: #2a2a2b"
  213. @click="addBlock(1)"
  214. >
  215. <template #icon>
  216. <icon-check-circle />
  217. </template>
  218. 启用选定的
  219. </a-button>
  220. </p>
  221. <p>
  222. <a-button
  223. type="text"
  224. class="button"
  225. style="color: #2a2a2b"
  226. @click="addBlock(0)"
  227. >
  228. <template #icon>
  229. <icon-close-circle />
  230. </template>
  231. 禁用选定的
  232. </a-button>
  233. </p>
  234. <a-divider style="margin: 10px 0" />
  235. <p>
  236. <a-button
  237. type="text"
  238. class="button"
  239. style="color: #2a2a2b"
  240. @click="addBlock(3)"
  241. >
  242. <template #icon>
  243. <icon-delete />
  244. </template>
  245. 删除选定的
  246. </a-button>
  247. </p>
  248. </template>
  249. </a-popover>
  250. <a-space
  251. direction="vertical"
  252. size="large"
  253. style="margin-right: 10px"
  254. class="space_search"
  255. v-show="!isBtn"
  256. >
  257. <a-input-search
  258. :style="{ width: '220px' }"
  259. v-model="form.keywords"
  260. @input="getList()"
  261. allow-clear
  262. @clear.stop="getList()"
  263. placeholder="搜索"
  264. />
  265. </a-space>
  266. <a-button
  267. type="outline"
  268. class="button"
  269. style="margin-right: 10px"
  270. v-show="isBtn"
  271. @click.stop="isBtn = !isBtn"
  272. >
  273. <template #icon>
  274. <icon-search />
  275. </template>
  276. </a-button>
  277. <a-popover position="bottom">
  278. <a-button
  279. type="outline"
  280. class="button"
  281. style="margin-right: 10px"
  282. >
  283. <icon-sort />
  284. </a-button>
  285. <template #content>
  286. <a-radio-group
  287. v-model="available_int"
  288. direction="vertical"
  289. size="large"
  290. @change="onChangeavailable"
  291. >
  292. <a-radio :value="''">所有</a-radio>
  293. <a-radio :value="1">启用</a-radio>
  294. <a-radio :value="0">禁用</a-radio>
  295. </a-radio-group>
  296. </template>
  297. </a-popover>
  298. <addDetails
  299. :item="item"
  300. @canplaythrough="canplaythrough"
  301. ></addDetails>
  302. </div>
  303. </div>
  304. <a-divider style="margin-top: 10px" />
  305. <div class="groupMain">
  306. <a-spin :loading="isLoading" :tip="tip">
  307. <a-checkbox-group v-model="data" @change="handleChange">
  308. <div
  309. class="groupList"
  310. :class="{
  311. groupActive: keyBg == index,
  312. groupNoActive: keyBg != index,
  313. }"
  314. v-for="(item, index) in listData"
  315. :key="index"
  316. >
  317. <a-checkbox :value="item.chunk_id"></a-checkbox>
  318. <div
  319. class="groupList-content emphaRed"
  320. @click="groupListContentClick(index)"
  321. @dblclick="contentClick(item)"
  322. v-html="item.content_with_weight"
  323. >
  324. </div>
  325. <div class="groupList-right">
  326. <a-switch
  327. v-model="item.available_int"
  328. :checked-value="1"
  329. :unchecked-value="0"
  330. @change="handleChangeStatus(item)"
  331. />
  332. </div>
  333. </div>
  334. </a-checkbox-group>
  335. </a-spin>
  336. </div>
  337. </div>
  338. <eidtDetails
  339. ref="eidtDil"
  340. :item="eidtDilinfo"
  341. v-model:eidtDilVisible="eidtDilVisible"
  342. @canplaythrough="canplaythrough"
  343. ></eidtDetails>
  344. </a-tab-pane>
  345. </a-tabs>
  346. </a-modal>
  347. </template>
  348. <script lang="ts" setup>
  349. import {
  350. onMounted,
  351. onBeforeMount,
  352. reactive,
  353. ref,
  354. computed,
  355. watch,
  356. } from 'vue';
  357. import { Message } from '@arco-design/web-vue';
  358. import {
  359. kbdocumentchangeparser,
  360. achunkCreate,
  361. queryChunkList,
  362. chunkSwitch,
  363. chunkRm,
  364. achunkGet,
  365. } from '@/api/kbList';
  366. import addDetails from '@/views/dmx/knowledgeLib/addDetails.vue';
  367. import eidtDetails from '@/views/dmx/knowledgeLib/eidtDetails.vue';
  368. const visible = ref(false);
  369. const loading = ref(false);
  370. const radio = ref('1');
  371. const keyBg = ref('');
  372. const eidtDil = ref();
  373. const eidtDilVisible = ref(false);
  374. const eidtDilinfo = reactive({});
  375. const props = defineProps(['kbtenantInfo', 'item', 'kbdetail']);
  376. // const emit = defineEmits(['upTabdateItem'])
  377. let threshold = computed(() => {
  378. return form.threshold / 100;
  379. });
  380. const kbtenantInfo = props.kbtenantInfo;
  381. let parser_ids = kbtenantInfo.parser_ids.split(',').map((item) => {
  382. const [value1, value2] = item.split(':');
  383. return {
  384. name: value2,
  385. value: value1,
  386. };
  387. });
  388. // 过滤解析方法
  389. const filterParserArr = [
  390. 'naive',
  391. 'qa',
  392. 'resume',
  393. 'manual',
  394. 'book',
  395. 'laws',
  396. 'one',
  397. ];
  398. const isBtn = ref(true);
  399. const closeSlelct = () => {
  400. window.addEventListener('click', (event) => {
  401. if (!event.target.closest('.space_search') && !isBtn.value) {
  402. isBtn.value = true;
  403. // form.keywords = '';
  404. }
  405. });
  406. };
  407. watch(isBtn, (val) => {
  408. if (!val) {
  409. closeSlelct();
  410. }
  411. });
  412. const form = reactive({
  413. parser_id: '',
  414. doc_id: props.item.id,
  415. max_token: 698,
  416. threshold: 0.3,
  417. max_cluster: 233,
  418. random_seed: 1500,
  419. chunk_token_num: 128,
  420. use_raptor: false,
  421. keywords: '',
  422. pages: {},
  423. prompt:
  424. '请总结以下段落。 小心数字,不要编造。 段落如下:\n' +
  425. ' {cluster_content}\n' +
  426. '以上就是你需要总结的内容。',
  427. });
  428. const formRef = ref(null);
  429. const rules = {
  430. prompt: [
  431. {
  432. required: true,
  433. message: '提示词不允许为空',
  434. },
  435. ],
  436. };
  437. const listData = ref([]);
  438. const formatter = (value) => {
  439. return value / 100;
  440. };
  441. function randomNumber() {
  442. // 生成一个介于min和max之间的随机整数(包含min和max)
  443. const min = 1; // 最小值
  444. const max = 10000; // 最大值
  445. form.random_seed = Math.floor(Math.random() * (max - min + 1)) + min;
  446. }
  447. const handleSubmit = async ({ values, errors }) => {
  448. if (!errors) {
  449. let data = await kbdocumentchangeparser({
  450. parser_id: form.parser_id,
  451. doc_id: form.doc_id,
  452. parser_config: {
  453. raptor: {
  454. use_raptor: form.use_raptor,
  455. prompt: form.prompt,
  456. max_token: form.max_token,
  457. threshold: form.threshold,
  458. max_cluster: form.max_cluster,
  459. random_seed: form.random_seed,
  460. },
  461. chunk_token_num: form.chunk_token_num,
  462. pages: [],
  463. },
  464. });
  465. if (data.code == 0) {
  466. Message.success('配置成功');
  467. } else {
  468. Message.error('配置失败');
  469. }
  470. visible.value = false;
  471. }
  472. };
  473. const handleClick = () => {
  474. visible.value = true;
  475. };
  476. const handleCancel = () => {
  477. visible.value = false;
  478. };
  479. const contentClick = async (item) => {
  480. try {
  481. const data = await achunkGet({
  482. chunk_id: item.chunk_id,
  483. });
  484. // console.log(data.data);
  485. Object.assign(eidtDilinfo, data.data);
  486. eidtDilVisible.value = true;
  487. } catch (error) {
  488. Message.error('获取失败');
  489. }
  490. };
  491. const handleOpened = (el) => {
  492. // console.log('props',props.kbtenantInfo);
  493. console.log('props', props.item);
  494. // console.log('props',props.kbdetail);
  495. console.log(parser_ids, 'parser_ids');
  496. let parser_id = props.item.parser_id;
  497. parser_ids.forEach((item) => {
  498. if (item.name == parser_id) {
  499. form.parser_id = item.value;
  500. }
  501. });
  502. // Object.assign(form,{
  503. // name: '',// 用户名
  504. // nameJoin: '',// 昵称
  505. // post: '',// 岗位
  506. // txt: '',// 备注
  507. // });
  508. // formRef.value.resetFields();
  509. console.log(props.item.id);
  510. };
  511. const onChangeRAPTOR = () => {};
  512. const indeterminate = ref(false);
  513. const checkedAll = ref(false);
  514. const data = ref([]);
  515. const switchType = ref(false);
  516. const available_int = ref('');
  517. const onRadioChange = () => {
  518. console.log(radio.value);
  519. };
  520. //批量操作
  521. const handleChangeAll = (value) => {
  522. indeterminate.value = false;
  523. if (value) {
  524. checkedAll.value = true;
  525. listData.value.forEach((item) => {
  526. data.value.push(item.chunk_id);
  527. });
  528. } else {
  529. checkedAll.value = false;
  530. data.value = [];
  531. }
  532. };
  533. const handleChange = (values) => {
  534. data.value = values;
  535. };
  536. //批量启用删除禁用
  537. const addBlock = (type) => {
  538. if (data.value.length == 0) {
  539. Message.error('请选择要操作的数据');
  540. return;
  541. }
  542. if (type == 3) {
  543. deleteChunk();
  544. } else {
  545. let params = {
  546. chunk_ids: data.value,
  547. available_int: type,
  548. doc_id: props.item.id,
  549. };
  550. chunkSwitch(params).then((res) => {
  551. if (res.code == 0) {
  552. Message.success('操作成功');
  553. getList();
  554. checkedAll.value = false;
  555. data.value = [];
  556. } else {
  557. Message.error('操作失败');
  558. }
  559. });
  560. }
  561. };
  562. const groupListContentClick = (value) => {
  563. keyBg.value = value;
  564. };
  565. const onChangeavailable = () => {
  566. getList();
  567. };
  568. const handleChangeStatus = (val) => {
  569. updateChunkStatus(val);
  570. };
  571. const isLoading = ref(true);
  572. // 自定义加载提示文本
  573. const tip = '加载中,请稍候...';
  574. //获取列表
  575. const getList = async () => {
  576. let params = {
  577. doc_id: props.item.id,
  578. keywords: form.keywords,
  579. page: 1,
  580. size: 10000,
  581. };
  582. if (available_int.value !== '') {
  583. params.available_int = available_int.value;
  584. }
  585. await queryChunkList(params).then((res) => {
  586. if (res.code == 0) {
  587. listData.value = res.data.chunks;
  588. isLoading.value = false;
  589. listData.value = listData.value.map((item) => {
  590. item.available_int = parseInt(item.available_int);
  591. return item;
  592. });
  593. }
  594. });
  595. };
  596. //更新列表
  597. const canplaythrough = () => {
  598. getList();
  599. };
  600. //启用禁用
  601. const updateChunkStatus = (val) => {
  602. let params = {
  603. chunk_ids: [val.chunk_id],
  604. available_int: val.available_int,
  605. doc_id: props.item.id,
  606. };
  607. chunkSwitch(params).then((res) => {
  608. console.log(res);
  609. if (res.code == 0) {
  610. Message.success('更新成功');
  611. getList();
  612. } else {
  613. Message.error('更新失败');
  614. }
  615. });
  616. };
  617. //删除
  618. const deleteChunk = () => {
  619. let params = {
  620. chunk_ids: data.value,
  621. doc_id: props.item.id,
  622. };
  623. chunkRm(params).then((res) => {
  624. console.log(res);
  625. if (res.code == 0) {
  626. Message.success('删除成功');
  627. getList();
  628. } else {
  629. Message.error('删除失败');
  630. }
  631. });
  632. };
  633. onBeforeMount(() => {
  634. getList();
  635. });
  636. onMounted(() => {});
  637. </script>
  638. <style scoped lang="less">
  639. .parser {
  640. width: 100%;
  641. ::v-deep .arco-btn-outline:hover,
  642. .arco-btn-outline,
  643. .arco-btn-outline[type='button'] {
  644. color: #2a2a2b;
  645. border: 1px solid #2a2a2b;
  646. }
  647. .button {
  648. //color: #2a2a2b!important;
  649. }
  650. }
  651. ::-webkit-scrollbar {
  652. display: none;
  653. }
  654. .groupMain {
  655. width: 100%;
  656. height: 500px;
  657. overflow: auto;
  658. display: flex;
  659. justify-content: center;
  660. .groupList {
  661. display: flex;
  662. padding: 24px;
  663. border-radius: 8px;
  664. //background: #eff8ff;
  665. border: 1px solid #f0f0f0;
  666. margin-bottom: 20px;
  667. overflow: auto;
  668. .groupList-content {
  669. width: 700px;
  670. //height: 100px;
  671. overflow: auto;
  672. box-sizing: border-box;
  673. margin: 0;
  674. padding: 0;
  675. color: #2a2a2b;
  676. font-size: 14px;
  677. font-family: Inter;
  678. position: relative;
  679. }
  680. }
  681. }
  682. :deep(.emphaRed em) {
  683. color: red !important;
  684. }
  685. .groupActive {
  686. background: #eff8ff;
  687. }
  688. .groupNoActive {
  689. background: var(--color-bg-1);
  690. color: var(--color-text-1);
  691. }
  692. </style>