MessageBody.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. <template>
  2. <div class="body-container" ref="body_container">
  3. <CardItem
  4. class="card_box"
  5. v-for="item in moke_data"
  6. :key="item.id"
  7. :name="item.name"
  8. :img_src="item.img_src"
  9. :height="195"
  10. @clickIcon="editIconHandle(item)"
  11. >
  12. <template #description> {{ item.desc }} </template>
  13. <template #settings>
  14. <a-switch v-model="item.online" checked-color="#4569EA" @change="changeStatus(item)">
  15. <template #checked> {{ $t('management.online') }} </template>
  16. <template #unchecked> {{ $t('management.offline') }} </template>
  17. </a-switch>
  18. </template>
  19. <template #labelBtn>
  20. <a-dropdown
  21. ref="dropdown"
  22. position="bl"
  23. :popup-max-height="false"
  24. @popup-visible-change="changeDropdownVisible($event, item.id)"
  25. >
  26. <div v-if="!item.label.length" class="label-btn">
  27. <img class="icon" src="../../assets/image/management/label.png" />
  28. <span class="text">{{ $t('management.addLabelBtn') }}</span>
  29. </div>
  30. <div v-else class="label-btn btn-content">
  31. <img class="icon" src="../../assets/image/management/label.png" />
  32. <span class="text">{{ item.label.map((it) => it.labelName).join(',') }}</span>
  33. </div>
  34. <template #content>
  35. <div class="label-add">
  36. <LabelSelected
  37. :labels="label_list"
  38. :addedLabels="item.label"
  39. @openManageTags="openManageTags"
  40. @toSearchLabels="toSearchLabels"
  41. @addLabelToTarget="addLabelToTarget($event, item.id)"
  42. ></LabelSelected>
  43. </div>
  44. </template>
  45. </a-dropdown>
  46. </template>
  47. <template #bottom>
  48. <div class="bottom">
  49. <div class="user">
  50. <!-- <icon-user /> {{ $t('management.createUser') }}:{{ item.user }} -->
  51. </div>
  52. <!-- <div class="tools" v-if="item.online">
  53. <icon-more :style="{ cursor: 'pointer' }" />
  54. </div> -->
  55. <div class="tools" v-if="!item.online">
  56. <icon-settings :style="{ cursor: 'pointer' }" @click.stop="editIntelligent(item)" />
  57. <icon-delete :style="{ cursor: 'pointer' }" @click.stop="deleteIntelligent(item)" />
  58. </div>
  59. </div>
  60. </template>
  61. </CardItem>
  62. <div class="loading-tips">
  63. <a-spin v-if="scroll_bottom && pages.current < total_pages" :tip="$t('management.loadingDataTip')" />
  64. <div v-if="scroll_bottom && pages.current >= total_pages">{{ $t('management.noMoreData') }}</div>
  65. </div>
  66. </div>
  67. <a-spin class="init-loading" v-if="loading_data" :size="32" />
  68. <a-empty v-if="!moke_data.length"></a-empty>
  69. <ManageTags v-model:visible="visible" :labels="label_list" @updateLabels="updateLabels"></ManageTags>
  70. <DeleteModal :visible.sync="delete_visible" @ok="deleteModalOk" @cancel="delete_visible = false" />
  71. </template>
  72. <script setup>
  73. import { ref, reactive, onMounted, onBeforeUnmount } from 'vue';
  74. // import { Message, Modal } from '@arco-design/web-vue';
  75. import { Message } from '../../3rd-libs/arco-vue-libs';
  76. import CardItem from '../common/CardItem.vue';
  77. import ManageTags from './ManageTags.vue';
  78. import DeleteModal from '../../components/DeleteModal.vue';
  79. import { getIconIntelligent, getImageIntelligent } from '../common/icon-map.js';
  80. import LabelSelected from './LabelSelected.vue';
  81. import { getDialogListForFilter } from '../../apis/intelligent';
  82. import {
  83. bus_intelligentSearch,
  84. bus_intelligentEdit,
  85. bus_intelligentIconEdit,
  86. bus_intelligentLabelSync
  87. } from '../../base/event-bus';
  88. import { dialogType } from '../../views/manage/common';
  89. import { funcThrottle } from '../../utils/utils';
  90. import { getLabelList, addLabelToDialog, changeDialogStatus, deleteDialog } from '../../apis/intelligent';
  91. import { deleteDify, deleteRgFlow } from '../../apis/rgflow-dify';
  92. const moke_data = ref([
  93. // {
  94. // id: 1,
  95. // name: '智能问答',
  96. // img_src: intelligentFrame1,
  97. // desc: '极目新闻记者了解到,这些患者都有一些共同的习惯:经常把未吃完的食物放在桌上敞着',
  98. // user: 'admin',
  99. // online: false,
  100. // },
  101. ]);
  102. const pages = reactive({
  103. current: 1,
  104. pageSize: 30
  105. });
  106. // 总页码
  107. const total_pages = ref(5);
  108. const body_container = ref(null);
  109. const scroll_bottom = ref(false);
  110. const search_text = ref('');
  111. const search_label = ref(''); //单个card的搜索标签
  112. const search_labelId_list = ref([]); //头部的搜索标签
  113. const search_mode = ref(''); //要过滤的应用类型
  114. const label_list = ref([]);
  115. const dropdown = ref(null);
  116. const visible = ref(false);
  117. const loading_data = ref(false);
  118. // 删除弹窗
  119. const delete_visible = ref(false);
  120. const delete_intelligent_item = ref(null);
  121. const getDialog = async () => {
  122. if (pages.current == 1) {
  123. loading_data.value = true;
  124. }
  125. try {
  126. const filter_data = {
  127. current: pages.current,
  128. pageSize: pages.pageSize,
  129. keyword: search_text.value,
  130. label: search_labelId_list.value,
  131. mode: search_mode.value
  132. };
  133. const res = await getDialogListForFilter(filter_data);
  134. const data = res.data.rows.map((item) => {
  135. return {
  136. ...item,
  137. time: new Date(item.create_time).toLocaleString('zh-CN'),
  138. desc: item.description,
  139. img_src: getImageIntelligent(item.icon) || getIconIntelligent(item.icon),
  140. user: item.user.userName || '-',
  141. online: item.status == 1
  142. };
  143. });
  144. total_pages.value = Math.ceil(res.data.total / pages.pageSize);
  145. moke_data.value.push(...data);
  146. } catch (err) {
  147. console.log(err);
  148. } finally {
  149. loading_data.value = false;
  150. }
  151. };
  152. const scrollHandler = (e) => {
  153. if (e.target.scrollTop + e.target.clientHeight >= e.target.scrollHeight) {
  154. scroll_bottom.value = true;
  155. // 调用函数加载更多数据
  156. if (pages.current < total_pages.value) {
  157. pages.current += 1;
  158. getDialog();
  159. } else {
  160. }
  161. }
  162. };
  163. const openManageTags = () => {
  164. visible.value = true;
  165. };
  166. const editIntelligent = (item) => {
  167. bus_intelligentEdit.emit(item);
  168. };
  169. const deleteIntelligent = (item) => {
  170. delete_intelligent_item.value = item;
  171. delete_visible.value = true;
  172. };
  173. const deleteModalOk = async () => {
  174. if (delete_intelligent_item.value.agentType == dialogType.DIFY) {
  175. await deleteDify(delete_intelligent_item.value.id);
  176. const response = await deleteDialog(delete_intelligent_item.value.id);
  177. if (response.code == 200) {
  178. Message.success(t('management.delete_success'));
  179. }
  180. // TODO:刷新页面
  181. } else if (delete_intelligent_item.value.agentType == dialogType.RAGFLOW) {
  182. const data = {
  183. dialog_ids: [delete_intelligent_item.value.id]
  184. };
  185. const res = await deleteRgFlow(data);
  186. if (res.code == 0) {
  187. const response = await deleteDialog(delete_intelligent_item.value.id);
  188. if (response.code == 200) {
  189. Message.success(t('management.delete_success'));
  190. }
  191. }
  192. }
  193. bus_intelligentSearch.emit('');
  194. delete_visible.value = false;
  195. };
  196. // 获取标签列表
  197. const getLabels = async () => {
  198. try {
  199. const res = await getLabelList(search_label.value, 1);
  200. if (res.code == 200) {
  201. label_list.value = [...res.data.rows];
  202. // 将label_list数据给到top中
  203. bus_intelligentLabelSync.emit(label_list.value);
  204. }
  205. } catch (err) {
  206. console.log(err);
  207. }
  208. };
  209. const updateLabels = () => {
  210. search_label.value = '';
  211. getLabels();
  212. };
  213. const toSearchLabels = (text) => {
  214. search_label.value = text;
  215. getLabels();
  216. };
  217. // 添加标签到智能体
  218. const addLabelToTarget = async (label_id_list, target_id) => {
  219. try {
  220. await addLabelToDialog(label_id_list, target_id);
  221. } catch (err) {
  222. console.log(err);
  223. }
  224. };
  225. // 当下拉框隐藏时,重新更新对应的智能体数据
  226. const changeDropdownVisible = async (visible, id) => {
  227. updateLabels();
  228. if (!visible) {
  229. const res = await getLabelList(search_label.value, 1, id);
  230. if (res.code == 200) {
  231. moke_data.value.forEach((item) => {
  232. if (item.id === id) {
  233. item.label = [...res.data.rows];
  234. }
  235. });
  236. }
  237. }
  238. };
  239. const changeStatus = async (item) => {
  240. try {
  241. const res = await changeDialogStatus(item.id, item.status == '1' ? '0' : '1');
  242. if (res.code != 200) {
  243. moke_data.value.forEach((it) => {
  244. if (item.id === id) {
  245. it.online = item.online;
  246. }
  247. });
  248. }
  249. } catch (err) {
  250. console.log(err);
  251. moke_data.value.forEach((it) => {
  252. if (it.id === item.id) {
  253. it.online = item.online;
  254. }
  255. });
  256. }
  257. };
  258. const editIconHandle = (item) => {
  259. bus_intelligentIconEdit.emit(item);
  260. };
  261. bus_intelligentSearch.on((search_data) => {
  262. search_text.value = search_data.searchText;
  263. search_labelId_list.value = search_data.target_label_list;
  264. search_mode.value = search_data.target_app;
  265. moke_data.value = [];
  266. pages.current = 1;
  267. getDialog();
  268. });
  269. onMounted(() => {
  270. getDialog();
  271. getLabels();
  272. body_container.value.addEventListener('scroll', funcThrottle(scrollHandler, 500));
  273. });
  274. onBeforeUnmount(() => {
  275. body_container.value.removeEventListener('scroll', scrollHandler);
  276. bus_intelligentSearch.off();
  277. });
  278. </script>
  279. <style scoped lang="css">
  280. .body-container {
  281. display: grid;
  282. grid-template-columns: repeat(6, 1fr);
  283. align-content: flex-start;
  284. gap: 20px;
  285. padding-top: 10px;
  286. overflow-y: auto;
  287. height: calc(100% - 90px);
  288. }
  289. .body-container::-webkit-scrollbar {
  290. display: none;
  291. }
  292. .label-btn {
  293. position: relative;
  294. background-color: var(--color-bg-2);
  295. color: var(--color-text-3);
  296. font-size: 12px;
  297. cursor: pointer;
  298. width: calc(100% - 60px);
  299. height: 32px;
  300. line-height: 32px;
  301. padding: 0px 30px;
  302. overflow: hidden;
  303. display: -webkit-box;
  304. -webkit-line-clamp: 1;
  305. -webkit-box-orient: vertical;
  306. text-overflow: ellipsis;
  307. visibility: hidden;
  308. .icon {
  309. position: absolute;
  310. top: 8px;
  311. left: 15px;
  312. }
  313. }
  314. .label-btn:hover {
  315. background-color: var(--color-fill-2);
  316. }
  317. .label-add {
  318. width: 240px;
  319. padding: 10px;
  320. }
  321. .bottom {
  322. display: flex;
  323. justify-content: space-between;
  324. align-items: center;
  325. }
  326. .loading-tips {
  327. grid-column: 1/-1;
  328. grid-row-start: auto;
  329. text-align: center;
  330. }
  331. .tools {
  332. display: flex;
  333. gap: 5px;
  334. visibility: hidden;
  335. }
  336. .card_box:hover .tools {
  337. cursor: pointer;
  338. visibility: visible;
  339. }
  340. .card_box:hover .label-btn {
  341. visibility: visible;
  342. }
  343. .btn-content {
  344. visibility: visible;
  345. background-color: var(--color-bg-1);
  346. }
  347. .arco-empty {
  348. position: absolute;
  349. top: 50%;
  350. left: 50%;
  351. transform: translate(-50%, -50%);
  352. }
  353. .init-loading {
  354. position: absolute;
  355. top: 50%;
  356. left: 50%;
  357. transform: translate(-50%, -50%);
  358. }
  359. </style>