@gientech/modual 1.3.2 → 1.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. package/.editorconfig +38 -0
  2. package/.prettierignore +16 -0
  3. package/.prettierrc +17 -0
  4. package/README.md +129 -129
  5. package/USAGE.md +191 -0
  6. package/bash.exe.stackdump +40 -0
  7. package/components.json +21 -0
  8. package/dist/README.md +129 -0
  9. package/{assets → dist/assets}/database.svg +11 -11
  10. package/{assets → dist/assets}/database_add.svg +53 -53
  11. package/{assets → dist/assets}/database_connect.svg +66 -66
  12. package/{assets → dist/assets}/database_upload.svg +29 -29
  13. package/{assets → dist/assets}/defaultWeLogo.svg +14 -14
  14. package/{assets/index-ldqIbm0x.js → dist/assets/index-XvC_4jDB.js} +106 -101
  15. package/{assets/index-D-dGaGjW.js → dist/assets/index-mPgEc8KC.js} +288 -102
  16. package/{assets/MySQL.svg → dist/assets/mysql.svg} +14 -14
  17. package/dist/assets/style.css +1 -0
  18. package/dist/assets/style3.css +1 -0
  19. package/{chat.js → dist/chat.js} +78 -78
  20. package/dist/database.js +20 -0
  21. package/{databaseId.js → dist/databaseId.js} +1 -1
  22. package/{databaseTable.js → dist/databaseTable.js} +1 -1
  23. package/{modelManage.js → dist/modelManage.js} +1 -1
  24. package/dist/package.json +56 -0
  25. package/{sensitive.js → dist/sensitive.js} +1 -1
  26. package/{streamFilesReader.js → dist/streamFilesReader.js} +11 -11
  27. package/{worker → dist/worker}/pdf.worker.min.js +21 -21
  28. package/doc_assets/2.png +0 -0
  29. package/doc_assets/demo.md +27 -0
  30. package/doc_assets/demos/dist-app/assets/index.Dh-ZAS9Z.css +2 -0
  31. package/doc_assets/demos/dist-app/assets/index.Dv8KVW18.js +23699 -0
  32. package/doc_assets/demos/dist-app/assets/index.Dv8KVW18.js.map +1 -0
  33. package/doc_assets/demos/dist-app/index.html +14 -0
  34. package/doc_assets/demos/dist-app/vite.svg +1 -0
  35. package/doc_assets/images/1.png +0 -0
  36. package/doc_assets/images/3.png +0 -0
  37. package/doc_assets/images/component-screenshot.png +1 -0
  38. package/doc_assets/install.md +5 -0
  39. package/eslint.config.js +92 -0
  40. package/index.html +13 -0
  41. package/package.json +83 -39
  42. package/postcss.config.cjs +19 -0
  43. package/public/vite.svg +1 -0
  44. package/public/worker/pdf.worker.min.js +22 -0
  45. package/scripts/README.md +133 -0
  46. package/scripts/build-demo.js +88 -0
  47. package/scripts/demo-selector.js +216 -0
  48. package/scripts/dev-demo.js +76 -0
  49. package/scripts/preview-demo.js +130 -0
  50. package/scripts/run-demo.bat +34 -0
  51. package/src/assets/img/downArrow.png +0 -0
  52. package/src/assets/img/excel.png +0 -0
  53. package/src/assets/img/img.png +0 -0
  54. package/src/assets/img/pdf.png +0 -0
  55. package/src/assets/img/ppt.png +0 -0
  56. package/src/assets/img/txt.png +0 -0
  57. package/src/assets/img/word.png +0 -0
  58. package/src/assets/login/homeBg.png +0 -0
  59. package/src/assets/login/left.jpg +0 -0
  60. package/src/assets/login/logoImg.png +0 -0
  61. package/src/examples/LoginPage/index.tsx +18 -0
  62. package/src/examples/aaa/index.tsx +3758 -0
  63. package/src/examples/chat/components/DrawerGraphPreview.tsx +78 -0
  64. package/src/examples/chat/index.tsx +190 -0
  65. package/src/examples/gientechStreamFilesReader/index.tsx +1016 -0
  66. package/src/examples/ragDatabaseDataPage/index.tsx +36 -0
  67. package/src/examples/ragDatabaseIdPage/index.tsx +44 -0
  68. package/src/examples/ragDatabasePage/index.tsx +36 -0
  69. package/src/examples/ragModelManagePage/index.tsx +37 -0
  70. package/src/examples/ragSearchPage/index.tsx +0 -0
  71. package/src/examples/ragSensitiveWordsPage/index.tsx +32 -0
  72. package/src/examples/streamFiles/index.tsx +384 -0
  73. package/src/lib_enter.ts +38 -0
  74. package/src/main.tsx +5 -0
  75. package/src/main.tsx.backup +5 -0
  76. package/src/modules/chat/Conversations/Item.tsx +167 -0
  77. package/src/modules/chat/Conversations/List.tsx +143 -0
  78. package/src/modules/chat/Conversations/groupByTime.ts +39 -0
  79. package/src/modules/chat/Conversations/index.tsx +212 -0
  80. package/src/modules/chat/constants.tsx +33 -0
  81. package/src/modules/chat/data.txt +82 -0
  82. package/src/modules/chat/index.tsx +1908 -0
  83. package/src/modules/chat/types.ts +17 -0
  84. package/src/modules/database/CreateModal.tsx +398 -0
  85. package/src/modules/database/assets/Doris.png +0 -0
  86. package/src/modules/database/assets/PostgreSQL.png +0 -0
  87. package/src/modules/database/assets/SQLServer.png +0 -0
  88. package/src/modules/database/assets/database.svg +11 -0
  89. package/src/modules/database/assets/database_add.svg +53 -0
  90. package/src/modules/database/assets/database_connect.svg +66 -0
  91. package/src/modules/database/assets/database_upload.svg +29 -0
  92. package/src/modules/database/assets/empty.png +0 -0
  93. package/src/modules/database/assets/mysql.svg +14 -0
  94. package/src/modules/database/index.tsx +466 -0
  95. package/src/modules/database/server.ts +196 -0
  96. package/src/modules/databaseId/CustomCom.tsx +156 -0
  97. package/src/modules/databaseId/EditConfig.tsx +268 -0
  98. package/src/modules/databaseId/UploadDrawer.tsx +520 -0
  99. package/src/modules/databaseId/assets/aiOptimize.svg +10 -0
  100. package/src/modules/databaseId/assets/empty.png +0 -0
  101. package/src/modules/databaseId/assets/template.svg +6 -0
  102. package/src/modules/databaseId/assets/upload.svg +9 -0
  103. package/src/modules/databaseId/assets/useTemp.svg +6 -0
  104. package/src/modules/databaseId/index.tsx +734 -0
  105. package/src/modules/databaseId/server.ts +286 -0
  106. package/src/modules/databaseId/style.css +5 -0
  107. package/src/modules/databaseTable/EditRowDrawer.tsx +119 -0
  108. package/src/modules/databaseTable/index.tsx +357 -0
  109. package/src/modules/databaseTable/server.ts +180 -0
  110. package/src/modules/headlessChat/constants.tsx +32 -0
  111. package/src/modules/headlessChat/index.tsx +1065 -0
  112. package/src/modules/headlessChat/types.ts +23 -0
  113. package/src/modules/login/components/Login/LoginBox/index.tsx +102 -0
  114. package/src/modules/login/components/Login/RegisterBox/index.tsx +180 -0
  115. package/src/modules/login/components/Login/index.tsx +100 -0
  116. package/src/modules/login/index.tsx +106 -0
  117. package/src/modules/login/style.css +3 -0
  118. package/src/modules/login/useServices.ts +53 -0
  119. package/src/modules/login/utils.ts +42 -0
  120. package/src/modules/modelManage/ConfigDrawer.tsx +249 -0
  121. package/src/modules/modelManage/assets/empty.png +0 -0
  122. package/src/modules/modelManage/const.ts +50 -0
  123. package/src/modules/modelManage/index.tsx +439 -0
  124. package/src/modules/modelManage/server.ts +185 -0
  125. package/src/modules/nodegraph/index.tsx +1 -0
  126. package/src/modules/search/assets/Icon-history.svg +8 -0
  127. package/src/modules/search/assets/answerAwartar.png +0 -0
  128. package/src/modules/search/assets/doc.png +0 -0
  129. package/src/modules/search/assets/genera.gif +0 -0
  130. package/src/modules/search/assets/icon-robot.svg +9 -0
  131. package/src/modules/search/assets/icon-search-bar.svg +14 -0
  132. package/src/modules/search/assets/icon-sub-title.svg +3 -0
  133. package/src/modules/search/assets/icon-title.svg +9 -0
  134. package/src/modules/search/assets/icon-zoomOut.svg +9 -0
  135. package/src/modules/search/assets/iconAi.svg +9 -0
  136. package/src/modules/search/assets/pdf.png +0 -0
  137. package/src/modules/search/assets/ppt.png +0 -0
  138. package/src/modules/search/assets/search.svg +3 -0
  139. package/src/modules/search/assets/selected.svg +4 -0
  140. package/src/modules/search/assets/txt.png +0 -0
  141. package/src/modules/search/assets/xls.png +0 -0
  142. package/src/modules/search/components/AssisSelect.tsx +137 -0
  143. package/src/modules/search/components/Editor/ChatViewEditor.tsx +261 -0
  144. package/src/modules/search/components/Editor/aichat.css +1 -0
  145. package/src/modules/search/components/Editor/constant.ts +13 -0
  146. package/src/modules/search/components/Editor/index.tsx +113 -0
  147. package/src/modules/search/components/Editor/plugins/autofomatRules.ts +332 -0
  148. package/src/modules/search/components/Editor/plugins/convertImgPlugins.tsx +20 -0
  149. package/src/modules/search/components/Editor/plugins/createIndexes.tsx +38 -0
  150. package/src/modules/search/components/Editor/plugins/displayer.ts +298 -0
  151. package/src/modules/search/components/Editor/plugins/imageClick.tsx +32 -0
  152. package/src/modules/search/components/Editor/plugins/myplugin.tsx +98 -0
  153. package/src/modules/search/components/Editor/ui/avatar.tsx +19 -0
  154. package/src/modules/search/components/Editor/ui/blockquote-element.tsx +21 -0
  155. package/src/modules/search/components/Editor/ui/button.tsx +58 -0
  156. package/src/modules/search/components/Editor/ui/calendar.tsx +68 -0
  157. package/src/modules/search/components/Editor/ui/caption.tsx +46 -0
  158. package/src/modules/search/components/Editor/ui/checkbox.tsx +27 -0
  159. package/src/modules/search/components/Editor/ui/code-block-combobox.tsx +188 -0
  160. package/src/modules/search/components/Editor/ui/code-block-element.css +434 -0
  161. package/src/modules/search/components/Editor/ui/code-block-element.tsx +39 -0
  162. package/src/modules/search/components/Editor/ui/code-leaf.tsx +24 -0
  163. package/src/modules/search/components/Editor/ui/code-line-element.tsx +10 -0
  164. package/src/modules/search/components/Editor/ui/code-syntax-leaf.tsx +21 -0
  165. package/src/modules/search/components/Editor/ui/column-element.tsx +30 -0
  166. package/src/modules/search/components/Editor/ui/column-group-element.tsx +94 -0
  167. package/src/modules/search/components/Editor/ui/command.tsx +75 -0
  168. package/src/modules/search/components/Editor/ui/comment-avatar.tsx +22 -0
  169. package/src/modules/search/components/Editor/ui/comment-create-form.tsx +37 -0
  170. package/src/modules/search/components/Editor/ui/comment-item.tsx +74 -0
  171. package/src/modules/search/components/Editor/ui/comment-leaf.tsx +49 -0
  172. package/src/modules/search/components/Editor/ui/comment-more-dropdown.tsx +42 -0
  173. package/src/modules/search/components/Editor/ui/comment-reply-items.tsx +22 -0
  174. package/src/modules/search/components/Editor/ui/comment-resolve-button.tsx +32 -0
  175. package/src/modules/search/components/Editor/ui/comment-value.tsx +34 -0
  176. package/src/modules/search/components/Editor/ui/comments-popover.tsx +63 -0
  177. package/src/modules/search/components/Editor/ui/date-element.tsx +83 -0
  178. package/src/modules/search/components/Editor/ui/dialog.tsx +63 -0
  179. package/src/modules/search/components/Editor/ui/draggable.tsx +177 -0
  180. package/src/modules/search/components/Editor/ui/dropdown-menu.tsx +180 -0
  181. package/src/modules/search/components/Editor/ui/emoji-input-element.tsx +85 -0
  182. package/src/modules/search/components/Editor/ui/excalidraw-element.tsx +28 -0
  183. package/src/modules/search/components/Editor/ui/fixed-toolbar-buttons.tsx +76 -0
  184. package/src/modules/search/components/Editor/ui/fixed-toolbar.tsx +8 -0
  185. package/src/modules/search/components/Editor/ui/floating-toolbar-buttons.tsx +51 -0
  186. package/src/modules/search/components/Editor/ui/floating-toolbar.tsx +77 -0
  187. package/src/modules/search/components/Editor/ui/heading-element.tsx +48 -0
  188. package/src/modules/search/components/Editor/ui/highlight-leaf.tsx +17 -0
  189. package/src/modules/search/components/Editor/ui/hr-element.tsx +30 -0
  190. package/src/modules/search/components/Editor/ui/icons.tsx +267 -0
  191. package/src/modules/search/components/Editor/ui/image-element.tsx +74 -0
  192. package/src/modules/search/components/Editor/ui/inline-combobox.tsx +368 -0
  193. package/src/modules/search/components/Editor/ui/input.tsx +25 -0
  194. package/src/modules/search/components/Editor/ui/insert-dropdown-menu.tsx +218 -0
  195. package/src/modules/search/components/Editor/ui/kbd-leaf.tsx +20 -0
  196. package/src/modules/search/components/Editor/ui/link-element.tsx +29 -0
  197. package/src/modules/search/components/Editor/ui/link-floating-toolbar.tsx +161 -0
  198. package/src/modules/search/components/Editor/ui/list-element.tsx +30 -0
  199. package/src/modules/search/components/Editor/ui/mark-toolbar-button.tsx +24 -0
  200. package/src/modules/search/components/Editor/ui/media-embed-element.tsx +133 -0
  201. package/src/modules/search/components/Editor/ui/media-popover.tsx +97 -0
  202. package/src/modules/search/components/Editor/ui/mention-element.tsx +43 -0
  203. package/src/modules/search/components/Editor/ui/mention-input-element.tsx +141 -0
  204. package/src/modules/search/components/Editor/ui/mode-dropdown-menu.tsx +93 -0
  205. package/src/modules/search/components/Editor/ui/more-dropdown-menu.tsx +67 -0
  206. package/src/modules/search/components/Editor/ui/paragraph-element.tsx +4 -0
  207. package/src/modules/search/components/Editor/ui/placeholder.tsx +52 -0
  208. package/src/modules/search/components/Editor/ui/popover.tsx +32 -0
  209. package/src/modules/search/components/Editor/ui/resizable.tsx +66 -0
  210. package/src/modules/search/components/Editor/ui/separator.tsx +25 -0
  211. package/src/modules/search/components/Editor/ui/style.less +12 -0
  212. package/src/modules/search/components/Editor/ui/table-cell-element.tsx +143 -0
  213. package/src/modules/search/components/Editor/ui/table-element.tsx +243 -0
  214. package/src/modules/search/components/Editor/ui/table-row-element.tsx +22 -0
  215. package/src/modules/search/components/Editor/ui/tableValue.tsx +135 -0
  216. package/src/modules/search/components/Editor/ui/todo-list-element.tsx +43 -0
  217. package/src/modules/search/components/Editor/ui/toggle-element.tsx +31 -0
  218. package/src/modules/search/components/Editor/ui/toolbar.tsx +157 -0
  219. package/src/modules/search/components/Editor/ui/tooltip.tsx +65 -0
  220. package/src/modules/search/components/Editor/ui/turn-into-dropdown-menu.tsx +160 -0
  221. package/src/modules/search/components/Editor/ui/with-draggables.tsx +175 -0
  222. package/src/modules/search/components/FileList.tsx +287 -0
  223. package/src/modules/search/components/ImageGroupView/index.tsx +85 -0
  224. package/src/modules/search/components/ResultContent.tsx +232 -0
  225. package/src/modules/search/components/SearchInput.tsx +232 -0
  226. package/src/modules/search/components/SearchLanding.tsx +74 -0
  227. package/src/modules/search/components/SearchView.tsx +563 -0
  228. package/src/modules/search/components/SimpleEditor.tsx +158 -0
  229. package/src/modules/search/components/SimpleFileList.tsx +215 -0
  230. package/src/modules/search/index.tsx +10 -0
  231. package/src/modules/search/reademe.md +1 -0
  232. package/src/modules/search/servers/apis.tsx +19 -0
  233. package/src/modules/search/servers/index.ts +184 -0
  234. package/src/modules/search/style.less +503 -0
  235. package/src/modules/search/type.ts +22 -0
  236. package/src/modules/search/utils.ts +34 -0
  237. package/src/modules/sensitive/index.tsx +313 -0
  238. package/src/modules/sensitive/server.ts +122 -0
  239. package/src/modules/streamFilesReader/GientechStreamReader.tsx +1555 -0
  240. package/src/modules/streamFilesReader/components/Header/Toolbar.tsx +0 -0
  241. package/src/modules/streamFilesReader/components/Header/index.tsx +297 -0
  242. package/src/modules/streamFilesReader/index.tsx +3 -0
  243. package/src/style.css +6 -0
  244. package/src/type.d.ts +0 -0
  245. package/src/utils/commonFn.tsx +111 -0
  246. package/src/utils/gientechCommon/components/AppError.tsx +32 -0
  247. package/src/utils/gientechCommon/components/AppLoading.tsx +75 -0
  248. package/src/utils/gientechCommon/components/DeleteModal.tsx +75 -0
  249. package/src/utils/gientechCommon/components/DisplayError.tsx +33 -0
  250. package/src/utils/gientechCommon/components/DisplayLoading.tsx +38 -0
  251. package/src/utils/gientechCommon/components/FeedBackModal.tsx +310 -0
  252. package/src/utils/gientechCommon/components/FileCardCommon.tsx +82 -0
  253. package/src/utils/gientechCommon/components/FileManager/index.tsx +390 -0
  254. package/src/utils/gientechCommon/components/FileManager/style.css +5 -0
  255. package/src/utils/gientechCommon/components/Messages/GientechNewChatWelcome.tsx +296 -0
  256. package/src/utils/gientechCommon/components/Messages/ReferenceCard.tsx +339 -0
  257. package/src/utils/gientechCommon/components/Messages/RetriveItem.tsx +245 -0
  258. package/src/utils/gientechCommon/components/Messages/WebRetriveItem.tsx +209 -0
  259. package/src/utils/gientechCommon/components/Messages/defaultBot.png +0 -0
  260. package/src/utils/gientechCommon/components/Messages/defaultStyleSet.tsx +148 -0
  261. package/src/utils/gientechCommon/components/Messages/defaultWeLogo.svg +14 -0
  262. package/src/utils/gientechCommon/components/RenameModal.tsx +86 -0
  263. package/src/utils/gientechCommon/components/style.less +11 -0
  264. package/src/utils/gientechCommon/configs/commonConfig.ts +2 -0
  265. package/src/utils/gientechCommon/configs/senderConfig.ts +0 -0
  266. package/src/utils/gientechCommon/configs/stylesConfig.ts +142 -0
  267. package/src/utils/gientechCommon/hooks/AichatUseController.tsx +345 -0
  268. package/src/utils/gientechCommon/slate/converters/deserializers.ts +763 -0
  269. package/src/utils/gientechCommon/slate/converters/mockData.ts +232 -0
  270. package/src/utils/gientechCommon/slate/converters/slateConverters.ts +258 -0
  271. package/src/utils/gientechCommon/slate/richElements/index.tsx +499 -0
  272. package/src/utils/gientechCommon/utils/request.ts +37 -0
  273. package/src/utils/gientechCommon/utils/serverFn.ts +172 -0
  274. package/src/utils/index.tsx +126 -0
  275. package/src/utils/testconfigs/demologin/index.tsx +32 -0
  276. package/src/utils/testconfigs/index.ts +53 -0
  277. package/src/vite-env.d.ts +11 -0
  278. package/stats.html +4949 -0
  279. package/tailwind.config.js +170 -0
  280. package/tsconfig.app.json +30 -0
  281. package/tsconfig.app.tsbuildinfo +11 -0
  282. package/tsconfig.json +13 -0
  283. package/tsconfig.node.json +22 -0
  284. package/tsconfig.node.tsbuildinfo +1 -0
  285. package/vite.config.ts +177 -0
  286. package/workflows/release.yml +60 -0
  287. package/assets/style.css +0 -1
  288. package/assets/style3.css +0 -1
  289. package/database.js +0 -20
  290. /package/{assets → dist/assets}/Doris.png +0 -0
  291. /package/{assets → dist/assets}/PostgreSQL.png +0 -0
  292. /package/{assets → dist/assets}/SQLServer.png +0 -0
  293. /package/{assets → dist/assets}/_commonjsHelpers-gnU0ypJ3.js +0 -0
  294. /package/{assets → dist/assets}/circle-alert-g2Y6zAjt.js +0 -0
  295. /package/{assets → dist/assets}/empty.png +0 -0
  296. /package/{assets → dist/assets}/index-CEK88UzR.js +0 -0
  297. /package/{assets → dist/assets}/index-CpW6Dhpp.js +0 -0
  298. /package/{assets → dist/assets}/plus-omCUN0e3.js +0 -0
  299. /package/{assets → dist/assets}/style2.css +0 -0
  300. /package/{assets → dist/assets}/styled-components.browser.esm-DPkS13KC.js +0 -0
  301. /package/{assets → dist/assets}/x-vPcWt3fC.js +0 -0
  302. /package/{chat.d.ts → dist/chat.d.ts} +0 -0
  303. /package/{database.d.ts → dist/database.d.ts} +0 -0
  304. /package/{databaseId.d.ts → dist/databaseId.d.ts} +0 -0
  305. /package/{databaseTable.d.ts → dist/databaseTable.d.ts} +0 -0
  306. /package/{modelManage.d.ts → dist/modelManage.d.ts} +0 -0
  307. /package/{sensitive.d.ts → dist/sensitive.d.ts} +0 -0
  308. /package/{streamFilesReader.d.ts → dist/streamFilesReader.d.ts} +0 -0
  309. /package/{vite.svg → dist/vite.svg} +0 -0
@@ -0,0 +1,1908 @@
1
+ import React, { useEffect, useState, useMemo, useRef } from 'react';
2
+ import axios from 'axios';
3
+ import { AiChat, type AppStatusManager } from '@mxmweb/aichat';
4
+ import { ChatMessageAdapter } from '@mxmweb/rtext';
5
+ import { message } from 'antd';
6
+ import { uid } from 'uid';
7
+ import { History } from 'lucide-react';
8
+ import '@mxmweb/rtext/style.css';
9
+ import '@mxmweb/aichat/style.css';
10
+ import '@mxmweb/zui/style.css';
11
+ import { defaultTheme, deepMergeTheme, type Styles } from '@mxmweb/zui';
12
+
13
+ import { DefaultSenderConfig } from './constants';
14
+ import GientechConversationPanel from './Conversations';
15
+ import {
16
+ convertQueryReplyPairListToMessages,
17
+ getFileTypeByName,
18
+ toCopy,
19
+ } from '../../utils/commonFn';
20
+ import {
21
+ getFeedbackList,
22
+ handleCancelFeedBack,
23
+ handleDeleteConversation,
24
+ handleFeedBack,
25
+ handleRename,
26
+ removeFilefromTempUpload,
27
+ handleCreateConversation,
28
+ } from '../../utils/gientechCommon/utils/serverFn';
29
+
30
+ import GientechNewChatWelcome from '../../utils/gientechCommon/components/Messages/GientechNewChatWelcome';
31
+ import AichatUseController from '../../utils/gientechCommon/hooks/AichatUseController';
32
+ import FeedBackModal from '../../utils/gientechCommon/components/FeedBackModal';
33
+ import FileManager from '../../utils/gientechCommon/components/FileManager';
34
+ import AppLoading from '../../utils/gientechCommon/components/AppLoading';
35
+ import DisplayLoading from '../../utils/gientechCommon/components/DisplayLoading';
36
+ import AppError from '../../utils/gientechCommon/components/AppError';
37
+ import DisplayError from '../../utils/gientechCommon/components/DisplayError';
38
+ import { maxPollCount, maxPollInterval } from '../../utils/gientechCommon/configs/commonConfig';
39
+ import { RenameModal } from '../../utils/gientechCommon/components/RenameModal';
40
+ import { DeleteModal } from '../../utils/gientechCommon/components/DeleteModal';
41
+
42
+ interface SenderConfig {
43
+ actions?: Array<{
44
+ name: string;
45
+ icon?: React.ReactNode;
46
+ badgeCount?: number;
47
+ enabled?: boolean;
48
+ [key: string]: any;
49
+ }>;
50
+ switchs?: Array<{
51
+ name: string;
52
+ label: string;
53
+ enabled?: boolean;
54
+ [key: string]: any;
55
+ }>;
56
+ [key: string]: any;
57
+ }
58
+
59
+ // 自定义组件接口
60
+ interface CustomComponents {
61
+ LogoBox?: React.ComponentType<any> | null;
62
+ AiChatBox?: React.ComponentType<any>;
63
+ UserChatBox?: React.ComponentType<any>;
64
+ WelcomeComponent?: React.ComponentType<any>;
65
+ DisplayLoading?: React.ComponentType<any>;
66
+ DisplayError?: React.ComponentType<any>;
67
+ AppError?: React.ComponentType<any>;
68
+ AppLoading?: React.ComponentType<any>;
69
+ [key: string]: React.ComponentType<any> | null | undefined;
70
+ }
71
+
72
+ interface GientechChatAdopterProps {
73
+ token: string;
74
+ url?: string;
75
+ CSRFToken?: string;
76
+ styles?: Styles;
77
+ eventsEmit?: (eventName: string, data: any) => void;
78
+ CustomComponents?: CustomComponents;
79
+ senderConfig?: SenderConfig;
80
+ [key: string]: any;
81
+ }
82
+
83
+ export interface SidebarTabConfig {
84
+ key: string;
85
+ type: 'custom' | 'form';
86
+ label: string;
87
+ icon: React.ReactNode;
88
+ enabled?: boolean;
89
+ component?: React.ReactNode; // type=custom
90
+ formConfig?: any[]; // type=form
91
+ initialValues?: Record<string, any>;
92
+ onChange?: (values: any) => void;
93
+ [key: string]: any;
94
+ }
95
+
96
+ export default function withGientechChatAdopter(WrappedComponent = AiChat) {
97
+ /**
98
+ * GientechChatAdopter 高阶组件
99
+ * 封装了与Gientech后端服务的接口对接、状态管理、事件处理等核心业务逻辑
100
+ * @param {React.ComponentType} WrappedComponent - 需要包裹的基础聊天组件,默认为 AiChat
101
+ */
102
+ return function GientechChatAdopter({
103
+ token,
104
+ url = 'http://localhost:8888',
105
+ styles,
106
+ CSRFToken,
107
+ eventsEmit,
108
+ ...rest
109
+ }: GientechChatAdopterProps) {
110
+ // 统一归一化样式:使用 UI 库的 deepMergeTheme
111
+ const normalizedStyles: Styles = useMemo(() => {
112
+ const baseStyles: Styles = { theme: defaultTheme, mode: 'light' };
113
+ return deepMergeTheme(baseStyles, styles);
114
+ }, [styles]);
115
+ const [isRenameModalOpen, setisRenameModalOpen] = useState(false);
116
+ const [isRemoveModalOpen, setisRemoveModalOpen] = useState(false);
117
+ const [curEditData, setCurEditData] = useState<any>(null);
118
+ // =================================================================
119
+ // State Management - 状态管理
120
+ // =================================================================
121
+ const [conversationList, setConversationList] = useState<any[]>([]); // 对话列表
122
+ const [chatData, setChatData] = useState<any[]>([]); // 所有对话的聊天记录
123
+ const [activeSessionId, setActiveSessionId] = useState<string | undefined>(undefined); // 当前激活的对话ID
124
+ const [recommandQuestions, setRecommandQuestions] = useState<any[]>([]); // 推荐问题列表
125
+ const [assistantList, setAssistantList] = useState<any[]>([]); // 智能体(助手)列表
126
+ const [newAssistantId, setNewAssistantId] = useState<string | undefined>(undefined); // 新建对话时选择的智能体ID
127
+ const [fileManagerOpen, setFileManagerOpen] = useState<boolean>(false); // 文件管理器抽屉的开关状态
128
+ const [appStatus, setAppStatus] = useState<AppStatusManager>({
129
+ display: 'ready', // 主聊天区域状态: ready, loading, processing, error, uploading
130
+ sender: 'ready', // 发送器状态: ready, processing, error, uploading
131
+ app: 'initializing', // 应用整体状态: initializing, ready, error
132
+ });
133
+ const [pendingFiles, setPendingFiles] = useState<any[]>([]); // 准备上传的文件列表
134
+ // 发送器配置支持外部传入
135
+ // 深度合并工具,customConfig 有的字段覆盖 defaultConfig
136
+ function deepMerge(defaultConfig: any, customConfig: any): any {
137
+ if (!customConfig) return defaultConfig;
138
+ const result: any = Array.isArray(defaultConfig) ? [...defaultConfig] : { ...defaultConfig };
139
+ for (const key in customConfig) {
140
+ if (
141
+ customConfig[key] !== undefined &&
142
+ customConfig[key] !== null &&
143
+ Object.prototype.hasOwnProperty.call(customConfig, key)
144
+ ) {
145
+ if (
146
+ typeof defaultConfig[key] === 'object' &&
147
+ defaultConfig[key] !== null &&
148
+ !Array.isArray(defaultConfig[key]) &&
149
+ typeof customConfig[key] === 'object' &&
150
+ customConfig[key] !== null &&
151
+ !Array.isArray(customConfig[key])
152
+ ) {
153
+ result[key] = deepMerge(defaultConfig[key], customConfig[key]);
154
+ } else {
155
+ result[key] = customConfig[key];
156
+ }
157
+ }
158
+ }
159
+ return result;
160
+ }
161
+
162
+ // 发送器配置支持外部传入,merge 默认和外部
163
+ const [dynamicSenderConfig, setDynamicSenderConfig] = useState(() =>
164
+ deepMerge(DefaultSenderConfig, rest.senderConfig)
165
+ );
166
+
167
+ // 监听外部 senderConfig 变化自动 merge
168
+ useEffect(() => {
169
+ if (rest.senderConfig) {
170
+ // console.log('hahah', dynamicSenderConfig, rest.senderConfig);
171
+ setDynamicSenderConfig(deepMerge(DefaultSenderConfig, rest.senderConfig));
172
+ }
173
+ }, [rest.senderConfig]);
174
+ const [fileStatuses, setFileStatuses] = useState<any[]>([]); // 用于跟踪每个上传文件的状态
175
+ const [fileManagerData, setFileManagerData] = useState<{ uploadedFiles: any[] }>(() => {
176
+ let uploaded: any[] = [];
177
+ return { uploadedFiles: uploaded };
178
+ });
179
+
180
+ useEffect(() => {
181
+ if (!activeSessionId) return;
182
+ const conv = chatData.find((c: any) => c.id === activeSessionId);
183
+ let uploaded: any[] = [];
184
+ let hasReference = false;
185
+ if (conv && conv.messages) {
186
+ const lastAiMsg = [...(conv.messages || [])]
187
+ .reverse()
188
+ .find((msg: any) => msg.istype === 'ai');
189
+ if (lastAiMsg?.reference && lastAiMsg.resultType === 5) {
190
+ hasReference = true;
191
+ try {
192
+ const fileList = JSON.parse(lastAiMsg.reference);
193
+ uploaded = fileList.map((f: any) => normalizeFileItem(f));
194
+ } catch (e) {}
195
+ }
196
+ }
197
+ if (hasReference) {
198
+ setFileManagerData({ uploadedFiles: uploaded });
199
+ }
200
+ // 否则不动,保持原有 uploadedFiles
201
+ }, [chatData]);
202
+
203
+ useEffect(() => {
204
+ setFileManagerData({ uploadedFiles: [] });
205
+ }, [activeSessionId]);
206
+
207
+ // 新增:全局管理助手选中状态,彻底解决子组件重建跳回默认
208
+ const [selectedAssistantId, setSelectedAssistantId] = React.useState(() => {
209
+ if (!assistantList || assistantList.length === 0) return undefined;
210
+ const defaultItem = assistantList.find(item => item.isDefault);
211
+ return defaultItem ? defaultItem.id : assistantList[0]?.id;
212
+ });
213
+
214
+ // 监听 assistantList 变化,自动选中默认助手
215
+ React.useEffect(() => {
216
+ if (!assistantList || assistantList.length === 0) return;
217
+ const exists = assistantList.some(item => item.id === selectedAssistantId);
218
+ if (!selectedAssistantId || !exists) {
219
+ const defaultItem = assistantList.find(item => item.isDefault);
220
+ setSelectedAssistantId(defaultItem ? defaultItem.id : assistantList[0].id);
221
+ }
222
+ }, [assistantList]);
223
+
224
+ // 新增:记录 sender switchs 的选中状态
225
+ const [senderSwitchValues, setSenderSwitchValues] = useState<{ [key: string]: boolean }>({});
226
+
227
+ // =================================================================
228
+ // Refs - 引用
229
+ // =================================================================
230
+ const prevActiveSessionId = useRef<string | undefined>(undefined); // 记录上一个激活的对话ID,用于对比变化
231
+ const setUploadedFilesRef = useRef<(files: any[]) => void>(undefined); // 引用核心组件的文件上传方法
232
+ const removeFileRef = useRef<(fileObj: any, idx: number, _type: string) => void>(undefined); // 引用核心组件的文件移除方法
233
+ const deleteData = useRef({ sessionId: '' }); // 暂存待删除的对话数据
234
+ const confirmed = useRef<any>(null); // 暂存 antd Modal.confirm 的引用
235
+ const [feedParam, setFeedParam] = useState<{
236
+ queryId?: number | string | undefined;
237
+ restult?: number;
238
+ }>({});
239
+ const [openFeed, setOpenFeed] = useState(false);
240
+ const [feedBackList, setFeedBackList] = useState<any>([]);
241
+ const stoppedRef = useRef(false);
242
+
243
+ // =================================================================
244
+ // Custom Hooks - 自定义钩子
245
+ // =================================================================
246
+ // 引入SSE控制器,封装了流式请求、消息管理等复杂逻辑
247
+ const { api_startChat_re, addEmptyMessage, setLastEmptyMessage, stopStream } =
248
+ AichatUseController({
249
+ baseUrl: url,
250
+ CSRFToken: CSRFToken,
251
+ token,
252
+ setChatData,
253
+ activeSessionId,
254
+ setAppStatus,
255
+ setConversationList,
256
+ });
257
+
258
+ // =================================================================
259
+ // Memoized Values - 缓存计算值
260
+ // =================================================================
261
+
262
+ /**
263
+ * 将智能体列表转换为以ID为键的Map,便于快速查找
264
+ */
265
+ const assistantMap = useMemo(() => {
266
+ const map: Record<string, any> = {};
267
+ (assistantList || []).forEach(item => {
268
+ map[item.id] = item;
269
+ });
270
+ return map;
271
+ }, [assistantList]);
272
+
273
+ /**
274
+ * 根据 activeSessionId 从 chatData 中筛选出当前对话的数据, 并合并智能体信息
275
+ */
276
+ const currentChatData = useMemo(() => {
277
+ const data = chatData.find((c: any) => c.id === activeSessionId) || {
278
+ id: activeSessionId,
279
+ messages: [],
280
+ };
281
+ const assistantInfo = data.configId ? assistantMap[data.configId] : null;
282
+ return {
283
+ ...data,
284
+ assistantInfo,
285
+ };
286
+ }, [chatData, activeSessionId, assistantMap]);
287
+
288
+ const is_download = useMemo(() => {
289
+ if (currentChatData.configId && assistantMap) {
290
+ const curAssist = assistantMap[currentChatData.configId];
291
+ if (curAssist?.configJson) {
292
+ const config = JSON.parse(curAssist.configJson);
293
+ return config.is_download;
294
+ }
295
+ return false;
296
+ } else {
297
+ return false;
298
+ }
299
+ }, [currentChatData.configId, assistantMap]);
300
+
301
+ const is_enableThinking = useMemo(() => {
302
+ //console.log('is_enableThinking',currentChatData.configId,assistantMap,newAssistantId)
303
+ if ((currentChatData.configId || newAssistantId) && assistantMap) {
304
+ const curAssist = assistantMap[currentChatData.configId];
305
+ if (curAssist?.configJson) {
306
+ const config = JSON.parse(curAssist.configJson);
307
+ console.log('is_enableThinking', currentChatData.configId, assistantMap, newAssistantId);
308
+ return config.thinking ? true : false;
309
+ } else {
310
+ // const curAssist = assistantMap[newAssistantId]
311
+ //console.log('newAssistantId',newAssistantId)
312
+ if (newAssistantId) {
313
+ const curAssist = assistantMap[newAssistantId];
314
+ if (curAssist?.configJson) {
315
+ const config = JSON.parse(curAssist.configJson);
316
+ return config.thinking ? true : false;
317
+ }
318
+ }
319
+ }
320
+ }
321
+ return false;
322
+ }, [currentChatData.configId, assistantMap, newAssistantId]);
323
+
324
+ // 合并文件工具,避免重复,且保证每个文件有 file 字段
325
+ function mergeFiles(oldFiles: any[], newFiles: any[]) {
326
+ const map = new Map();
327
+ oldFiles.forEach(f => map.set(f.uid, normalizeFileItem(f)));
328
+ newFiles.forEach(f => map.set(f.uid, normalizeFileItem(f)));
329
+ // console.log(Array.from(map.values()),'``````````````````9929312830918309812903819023890128390')
330
+ return Array.from(map.values());
331
+ }
332
+
333
+ // 文件结构标准化,保证有 file 字段
334
+ function normalizeFileItem(f: any) {
335
+ if (!f) return f;
336
+ if (f.file && f.file.name) return f;
337
+ // 兼容后端返回的扁平对象,优先 name,其次 fileName
338
+ const name = f.name || f.fileName || '';
339
+ return {
340
+ ...f,
341
+ url: f.filePath || '',
342
+ uid: f.uid || '',
343
+ file: {
344
+ name,
345
+ size:
346
+ typeof f.size === 'string' && f.size.endsWith('MB')
347
+ ? Number(f.size.replace('MB', '')) * 1024 * 1024
348
+ : f.size || 0,
349
+ type: f.type || '',
350
+ },
351
+ name, // 兜底 name 字段,供 UI 使用
352
+ };
353
+ }
354
+
355
+ // =================================================================
356
+ // Side Effects (useEffect) - 副作用钩子
357
+ // =================================================================
358
+
359
+ /**
360
+ * 跟踪 activeSessionId 的变化:
361
+ * 1. 清理上一个无消息的临时会话
362
+ * 2. 更新推荐问题
363
+ */
364
+ useEffect(() => {
365
+ // 切换会话时,检查上一个会话是否为临时且无消息
366
+ if (prevActiveSessionId.current && prevActiveSessionId.current !== activeSessionId) {
367
+ const prevId = prevActiveSessionId.current;
368
+ const prevConv = conversationList.find(c => c.sessionId === prevId && c.isNew);
369
+ const prevChat = chatData.find(c => c.id === prevId && c.isNew);
370
+ const prevHasMsg = prevChat && prevChat.messages && prevChat.messages.length > 0;
371
+ // 如果是临时的且没有消息,则自动删除
372
+ if (prevConv && !prevHasMsg) {
373
+ setConversationList(list => list.filter(c => c.sessionId !== prevId));
374
+ setChatData(list => list.filter(c => c.id !== prevId));
375
+ }
376
+ }
377
+ prevActiveSessionId.current = activeSessionId;
378
+
379
+ // 更新推荐问题为当前对话的最后一个推荐问题
380
+ const historyRQ =
381
+ currentChatData.messages && currentChatData.messages.length > 0
382
+ ? currentChatData.messages[currentChatData.messages.length - 1].recommendQuestion
383
+ : [];
384
+ setRecommandQuestions(historyRQ);
385
+ }, [activeSessionId, conversationList, chatData, currentChatData.messages]);
386
+
387
+ /**
388
+ * 首次加载时获取对话列表和智能体列表
389
+ */
390
+ useEffect(() => {
391
+ setAppStatus(prev => ({ ...prev, app: 'initializing' }));
392
+ Promise.all([
393
+ axios.get(`${url}/qa/dialogue/list?pageNo=1&pageSize=3000&sysType=INT_SESSION`, {
394
+ headers: { Authorization: token },
395
+ }),
396
+ axios.get(`${url}/qa/search/config/helper/list?pageNo=1&pageSize=3000`, {
397
+ headers: { Authorization: token },
398
+ }),
399
+ ])
400
+ .then(([convRes, assistantRes]) => {
401
+ const rawList = convRes.data.data.records || [];
402
+ setConversationList(rawList);
403
+ // // 如果有对话列表,默认选中第一个
404
+ // if (rawList.length > 0 && !activeSessionId) {
405
+ // setActiveSessionId(rawList[0].sessionId);
406
+ // }
407
+ // // 如果对话列表为空,自动新建一个欢迎会话
408
+ // if (rawList.length === 0) {
409
+
410
+ // 首次进入时直接开始新对话,不自动跳转到历史对话
411
+ if (!activeSessionId) {
412
+ handleConversationCreate({});
413
+ }
414
+ setAssistantList(assistantRes.data.data || []);
415
+ setAppStatus(prev => ({ ...prev, app: 'ready' }));
416
+ })
417
+ .catch(err => {
418
+ setAppStatus(prev => ({ ...prev, app: 'error' }));
419
+ });
420
+ }, [url, token]);
421
+
422
+ /**
423
+ * 当 activeSessionId 变化时,获取对应的聊天记录
424
+ */
425
+ useEffect(() => {
426
+ if (!activeSessionId) return;
427
+
428
+ setAppStatus(prev => ({ ...prev, display: 'loading' }));
429
+ axios
430
+ .get(`${url}/qa/dialogue/getDialogBySessionId?sessionId=${activeSessionId}`, {
431
+ headers: { Authorization: token },
432
+ })
433
+ .then(res => {
434
+ // 如果会话没有数据(例如新建的),直接返回
435
+
436
+ if (!res.data.data) {
437
+ setAppStatus(prev => ({ ...prev, display: 'ready' }));
438
+ return;
439
+ }
440
+ // 解析消息数据
441
+ const queryReplyPairList = res.data.data?.queryReplyPairList || [];
442
+ const messages = convertQueryReplyPairListToMessages(queryReplyPairList);
443
+ const {
444
+ sessionId,
445
+ searchConfigDTO,
446
+ filePath,
447
+ configId,
448
+ reference,
449
+ webReference,
450
+ uploadedFiles,
451
+ fileList,
452
+ currentFiles,
453
+ } = res.data.data;
454
+
455
+ // === 文件数据补全 ===
456
+ // 1. reference(AI消息)
457
+ if (messages.length > 0) {
458
+ // 找到最后一条 AI 消息的索引(从后往前找)
459
+ const lastAiIndex =
460
+ messages.length - 1 - [...messages].reverse().findIndex(m => m.istype === 'ai');
461
+
462
+ // 确保找到的是有效的 AI 消息
463
+ if (
464
+ lastAiIndex >= 0 &&
465
+ lastAiIndex < messages.length &&
466
+ messages[lastAiIndex].istype === 'ai'
467
+ ) {
468
+ if (reference) {
469
+ messages[lastAiIndex].reference = reference;
470
+ }
471
+ if (webReference) {
472
+ messages[lastAiIndex].webReference = webReference;
473
+ }
474
+ }
475
+ }
476
+ // 2. currentFiles/fileList/uploadedFiles(user消息)
477
+ const filesData = currentFiles || fileList || uploadedFiles;
478
+ if (filesData && messages.length > 0) {
479
+ const lastUserIdx = [...messages].reverse().findIndex(m => m.istype === 'user');
480
+ if (lastUserIdx !== -1) {
481
+ console.log(messages[messages.length - 1 - lastUserIdx].currentFiles, 'filesData');
482
+ messages[messages.length - 1 - lastUserIdx].currentFiles = filesData;
483
+ }
484
+ }
485
+
486
+ // 更新对话列表中的对应项的元数据
487
+ const item = conversationList.find(item => item.sessionId === sessionId);
488
+ if (item) {
489
+ // console.log(item,'你大爷')
490
+ setConversationList(list =>
491
+ list.map(item =>
492
+ item.sessionId === sessionId
493
+ ? { ...item, searchConfigDTO, filePath, configId }
494
+ : item
495
+ )
496
+ );
497
+ }
498
+
499
+ // 构造当前对话的完整数据
500
+ const conversationConfig = {
501
+ configId: res.data.data.configId,
502
+ filePath: res.data.data.filePath,
503
+ searchConfigDTO: res.data.data.searchConfigDTO || {},
504
+ sessionId: res.data.data.sessionId,
505
+ label: res.data.data.label,
506
+ kbId: res.data.data.kbId,
507
+ };
508
+
509
+ // 更新聊天数据
510
+ setChatData(prev => {
511
+ const others = prev.filter((c: any) => c.id !== activeSessionId);
512
+
513
+ const newChatData = [
514
+ { id: activeSessionId, messages, ...conversationConfig },
515
+ ...others,
516
+ ];
517
+ return newChatData;
518
+ });
519
+ setAppStatus(prev => ({ ...prev, display: 'ready' }));
520
+ })
521
+ .catch(err => {
522
+ console.error('获取聊天数据失败:', err);
523
+ if (err.response && err.response.status === 404) {
524
+ // 404 表示会话在后端不存在,按新建会话处理
525
+ handleConversationCreate({});
526
+ }
527
+ setAppStatus(prev => ({ ...prev, display: 'error' }));
528
+ });
529
+ }, [activeSessionId, url, token]);
530
+
531
+ /**
532
+ * 监听文件数据变化,更新Sender上的角标
533
+ */
534
+ useEffect(() => {
535
+ const totalFiles = fileManagerData.uploadedFiles.length;
536
+ setDynamicSenderConfig((prev: any) => {
537
+ const actions = Array.isArray(prev.actions) ? prev.actions : [];
538
+ const newConfig = {
539
+ ...prev,
540
+ actions: actions.map((action: any) =>
541
+ action.name === 'history' ? { ...action, badgeCount: totalFiles } : action
542
+ ),
543
+ };
544
+ return newConfig;
545
+ });
546
+ }, [fileManagerData.uploadedFiles]);
547
+ useEffect(() => {
548
+ getFeedbackList({ url, token }, data => {
549
+ setFeedBackList(data);
550
+ });
551
+ }, []);
552
+
553
+ /**
554
+ * 实时同步 fileStatuses 到当前消息的 currentFiles,便于消息区细致显示每个文件的处理状态
555
+ */
556
+ React.useEffect(() => {
557
+ if (!fileStatuses.length) return;
558
+ setChatData(chatList => {
559
+ if (!chatList[0]) return chatList;
560
+ const { messages } = chatList[0];
561
+ for (let i = messages.length - 1; i >= 0; i--) {
562
+ if (messages[i].istype === 'user') {
563
+ const oldFiles = messages[i].currentFiles || [];
564
+ messages[i].currentFiles = fileStatuses.map((f: any) => {
565
+ const old = oldFiles.find((of: any) => of.uid === f.uid);
566
+ return {
567
+ name: f.file?.name || f.name,
568
+ size: f.file?.size || f.size,
569
+ type: f.file?.type || f.type,
570
+ status: f.status,
571
+ uid: f.uid,
572
+ url: f.url || old?.url || '', // 优先用已有 url
573
+ };
574
+ });
575
+ break;
576
+ }
577
+ }
578
+ return [...chatList];
579
+ });
580
+ }, [fileStatuses]);
581
+
582
+ // =================================================================
583
+ // Core Functions - 核心功能函数
584
+ // =================================================================
585
+
586
+ // 1. 在组件作用域定义控制器和轮询停止标志
587
+ let uploadController: AbortController | null = null;
588
+ let stopPoll: (() => void) | null = null;
589
+
590
+ /**
591
+ * 轮询文件解析状态
592
+ * @param {string[]} uids - 文件UID列表
593
+ * @param {(serverStatuses: any[]) => void} onStatusUpdate - 每次获取到新状态后的回调
594
+ * @param {() => void} onComplete - 所有文件解析完成后的回调
595
+ * @param {(errorMsg: string) => void} onFail - 轮询失败或超时的回调
596
+ */
597
+ const pollFileStatus = (
598
+ uids: string[],
599
+ onStatusUpdate: (serverStatuses: any[]) => void,
600
+ onComplete: () => void,
601
+ onFail: (errorMsg: string) => void
602
+ ) => {
603
+ const uidsString = uids.join(',');
604
+ let pollCount = 0;
605
+ // const maxPollCount = 3000;
606
+ stopPoll = () => {
607
+ stoppedRef.current = true;
608
+ };
609
+
610
+ const checkStatus = async () => {
611
+ if (stoppedRef.current) return;
612
+ if (pollCount >= maxPollCount) {
613
+ if (!stoppedRef.current) onFail('文件解析超时,请稍后重试');
614
+ return;
615
+ }
616
+ pollCount++;
617
+ try {
618
+ const res = await axios.get(
619
+ `${url}/index/knowledgeBase/file/getFileStatusByUids?uids=${uidsString}`,
620
+ {
621
+ headers: { Authorization: token },
622
+ }
623
+ );
624
+ if (stoppedRef.current) return;
625
+ if (res.data.success) {
626
+ const statuses = res.data.data;
627
+ if (!stoppedRef.current) onStatusUpdate(statuses);
628
+ const allDone = statuses.every((file: any) => file.status === 3);
629
+ if (allDone) {
630
+ if (!stoppedRef.current) onComplete();
631
+ } else {
632
+ if (!stoppedRef.current) setTimeout(checkStatus, maxPollInterval);
633
+ }
634
+ } else {
635
+ if (!stoppedRef.current) onFail(res.data.errorMsg || '查询文件状态失败');
636
+ }
637
+ } catch (error) {
638
+ if (stoppedRef.current) return;
639
+ onFail('查询文件状态时发生网络错误');
640
+ }
641
+ };
642
+ checkStatus();
643
+ };
644
+
645
+ /**
646
+ * 设置消息的反馈结果(赞/踩)
647
+ * @param {string} queryId - 消息ID
648
+ * @param {number} result - 反馈结果 (1: 赞, -1: 踩)
649
+ */
650
+ const setFeed = (queryId: any, restult: any) => {
651
+ setChatData(chatList => {
652
+ const newList = [...chatList];
653
+ if (!newList[0]) return newList;
654
+ newList[0] = {
655
+ ...newList[0],
656
+ messages: newList[0].messages.map((item: any) => {
657
+ if (item.queryId === queryId) {
658
+ return {
659
+ ...item,
660
+ feedbackResult: restult,
661
+ };
662
+ }
663
+ return item;
664
+ }),
665
+ };
666
+ return newList;
667
+ });
668
+ };
669
+
670
+ /**
671
+ * 获取指定queryId的推荐问题
672
+ * @param {string} queryId - 消息ID
673
+ */
674
+ const fetchRecommendQuestions = async (queryId: string) => {
675
+ if (!queryId) return;
676
+ try {
677
+ // 需要携带当前 configId
678
+ const configId =
679
+ currentChatData?.configId || assistantMap?.[currentChatData?.configId || '']?.id || '';
680
+ const res = await axios.get(
681
+ `${url}/qa/ai/recommendQuestion?queryId=${queryId}&configId=${configId || ''}`.replace(
682
+ /\?&/,
683
+ '?'
684
+ ),
685
+ {
686
+ headers: { Authorization: token },
687
+ }
688
+ );
689
+ setRecommandQuestions(res.data?.data || []);
690
+ } catch (e) {
691
+ setRecommandQuestions([]);
692
+ }
693
+ };
694
+
695
+ /**
696
+ * 处理重新发送消息逻辑
697
+ */
698
+ function handleReSenderSend(data: any) {
699
+ const { content, audioUrl, filePaths, queryId } = data;
700
+ if (!activeSessionId || !queryId) return;
701
+
702
+ // 从当前会话中按 queryId 定位本次 AI 回复
703
+ const msgs = currentChatData?.messages || [];
704
+ const aiIdx = msgs.findIndex(
705
+ (m: any) => m?.queryId === queryId && (m?.istype === 'ai' || m?.isUser === false)
706
+ );
707
+
708
+ // 回溯上一条用户消息
709
+ let userMsg: any | undefined;
710
+ if (aiIdx > 0) {
711
+ for (let i = aiIdx - 1; i >= 0; i -= 1) {
712
+ const m = msgs[i];
713
+ if (m?.istype === 'user' || m?.isUser === true) {
714
+ userMsg = m;
715
+ break;
716
+ }
717
+ }
718
+ }
719
+
720
+ const question = userMsg?.content ?? content;
721
+ if (!question) return;
722
+
723
+ const sourceFilePaths = userMsg?.filePaths ?? filePaths;
724
+ const _fileUids = (
725
+ typeof sourceFilePaths === 'string' && sourceFilePaths
726
+ ? JSON.parse(sourceFilePaths)
727
+ : sourceFilePaths || []
728
+ ).map((item: any) => item.uid);
729
+ const now = Date.now();
730
+ setLastEmptyMessage({ now });
731
+ setAppStatus(pre => {
732
+ return {
733
+ ...pre,
734
+ display: 'processing',
735
+ sender: 'processing',
736
+ };
737
+ });
738
+
739
+ data.clearFn && data.clearFn();
740
+ setConversationList(list =>
741
+ list.map(c => (c.sessionId === activeSessionId ? { ...c, gmtModified: now } : c))
742
+ );
743
+
744
+ api_startChat_re(
745
+ {
746
+ configId: currentChatData.configId,
747
+ content: question,
748
+ fileUids: _fileUids,
749
+ lastDate: now,
750
+ name: currentChatData.label || '',
751
+ queryId: queryId,
752
+ sessionId: String(activeSessionId || ''),
753
+ type: (userMsg?.audioUrl ?? audioUrl) ? 'audioUrl' : 'text',
754
+ audioUrl: (userMsg?.audioUrl ?? audioUrl) || '',
755
+ enableThinking: !!senderSwitchValues.reasoning,
756
+ enableWebsearch: !!senderSwitchValues.netSearch,
757
+ qaChannel: senderSwitchValues.qaChannel,
758
+ } as any,
759
+ gmtModified => {
760
+ // 只在AI回复完成后请求推荐问题
761
+ fetchRecommendQuestions(queryId);
762
+ }
763
+ );
764
+ }
765
+
766
+ // 提取发消息逻辑,确保只有有sessionId时才调用
767
+ async function handleSenderSend(data: any, clearFn: () => void) {
768
+ // 找到当前会话
769
+ if (!activeSessionId) return;
770
+ const conv = conversationList.find(c => c.sessionId === activeSessionId);
771
+ //console.log('zheli是啥',conv)
772
+ if (conv && conv.isNew) {
773
+ // 还没落库,先调 create
774
+ let createSuccess = false;
775
+ // 优先用用户选择的助手newAssistantId,否则用当前会话的configId
776
+ const configIdToUse = newAssistantId || conv.configId;
777
+ // 用用户输入内容截取20字作为label
778
+ const labelToUse =
779
+ data && data.content ? data.content.slice(0, 20) : conv.label || '未命名会话';
780
+ await new Promise<void>((resolve, reject) => {
781
+ handleCreateConversation(
782
+ { url, token, CSRFToken },
783
+ {
784
+ label: labelToUse,
785
+ sessionId: conv.sessionId,
786
+ sysType: 'INT_SESSION',
787
+ configId: configIdToUse,
788
+ },
789
+ (res: any) => {
790
+ if (res && res.data && res.data.sessionId) {
791
+ // create成功,标记已落库并更新label/configId
792
+ setConversationList(list =>
793
+ list.map(c =>
794
+ c.sessionId === conv.sessionId
795
+ ? { ...c, isNew: false, configId: configIdToUse, label: labelToUse }
796
+ : c
797
+ )
798
+ );
799
+ setChatData(list =>
800
+ list.map(c =>
801
+ c.id === conv.sessionId
802
+ ? { ...c, isNew: false, configId: configIdToUse, label: labelToUse }
803
+ : c
804
+ )
805
+ );
806
+ createSuccess = true;
807
+ resolve();
808
+ } else {
809
+ message.error('创建会话失败,请重试');
810
+ reject();
811
+ }
812
+ }
813
+ );
814
+ });
815
+ if (!createSuccess) return;
816
+ // 继续发消息
817
+ sendMessageWithSessionId(activeSessionId as string, data, clearFn, configIdToUse);
818
+ return;
819
+ }
820
+ // 已落库,直接发消息
821
+ sendMessageWithSessionId(activeSessionId as string, data, clearFn);
822
+ }
823
+
824
+ // 修改sendMessageWithSessionId,发消息时用本地会话的configId
825
+ function sendMessageWithSessionId(
826
+ sessionId: string,
827
+ data: any,
828
+ clearFn: () => void,
829
+ configIdOverride?: string
830
+ ) {
831
+ const queryId = 'query-' + uid(32);
832
+ // 优先用参数传递的 configId
833
+ const conv = conversationList.find(c => c.sessionId === sessionId);
834
+ const configIdToUse = configIdOverride || conv?.configId || currentChatData.configId;
835
+ console.log('助手配置详情', configIdToUse);
836
+ const label = data && data.content ? data.content.slice(0, 20) : '未命名会话';
837
+ let localCurrentFiles = undefined;
838
+ if (data.files && data.files.length > 0) {
839
+ localCurrentFiles = data.files.map((fileItem: any) => ({
840
+ name: fileItem.file.name,
841
+ size: fileItem.file.size,
842
+ type: fileItem.file.type,
843
+ filePath: fileItem.filePath || '',
844
+ convertedFilePath: fileItem.convertedFilePath || '',
845
+ uid: fileItem.uid || '',
846
+ }));
847
+ }
848
+ console.log('本地文件上传', data.files);
849
+ addEmptyMessage({
850
+ content: data.content || data.text || '',
851
+ queryId,
852
+ currentFiles: localCurrentFiles,
853
+ filePaths: localCurrentFiles ? JSON.stringify(localCurrentFiles) : undefined,
854
+ });
855
+ // === 新增:如果当前会话是未命名且isNew,首次提问后自动改名并去除isNew ===
856
+ setConversationList(list =>
857
+ list.map(c =>
858
+ c.sessionId === sessionId && c.label === '未命名会话' && c.isNew
859
+ ? { ...c, label, gmtModified: Date.now(), isNew: false }
860
+ : c
861
+ )
862
+ );
863
+ setChatData(list =>
864
+ list.map(c =>
865
+ c.id === sessionId && c.label === '未命名会话' && c.isNew
866
+ ? { ...c, label, isNew: false }
867
+ : c
868
+ )
869
+ );
870
+ // 2. 文件上传逻辑 ---
871
+ if (data.files && data.files.length > 0) {
872
+ setAppStatus(pre => ({ ...pre, sender: 'uploading', display: 'uploading' }));
873
+ uploadController = new AbortController();
874
+ stoppedRef.current = false;
875
+ const clientFileUids = data.files.map(
876
+ () => `rc-upload-${Date.now()}-${Math.random().toString(36).slice(2)}`
877
+ );
878
+ const initialFileStatuses = data.files.map((fileItem: any, index: number) => ({
879
+ ...fileItem,
880
+ uid: clientFileUids[index],
881
+ status: 'uploading',
882
+ }));
883
+ setFileStatuses(initialFileStatuses);
884
+ const formData = new FormData();
885
+ data.files.forEach((fileItem: any) => {
886
+ formData.append('files', fileItem.file);
887
+ });
888
+ const sessionIdStr = String(sessionId || '');
889
+ formData.append('sessionId', sessionIdStr);
890
+ formData.append('uids', clientFileUids.join(','));
891
+ axios
892
+ .post(`${url}/index/knowledgeBase/file/uploadFiles`, formData, {
893
+ headers: {
894
+ Authorization: token,
895
+ 'Content-Type': 'multipart/form-data',
896
+ },
897
+ signal: uploadController.signal,
898
+ })
899
+ .then(response => {
900
+ console.log('文件上传的打印结果', response);
901
+ if (stoppedRef.current) return;
902
+ if (
903
+ response.data.success &&
904
+ Array.isArray(response.data.data) &&
905
+ response.data.data.length > 0
906
+ ) {
907
+ // === 新增:合并后端返回字段到 fileStatuses ===
908
+ setFileStatuses(prev => {
909
+ return prev.map(localFile => {
910
+ const serverFile = response.data.data.find((f: any) => f.uid === localFile.uid);
911
+ return serverFile ? { ...localFile, ...serverFile } : localFile;
912
+ });
913
+ });
914
+ setAppStatus(pre => ({ ...pre, sender: 'processing', display: 'analyzing' }));
915
+ setFileStatuses(prev => prev.map(f => ({ ...f, status: 'parsing' })));
916
+ const uploadedFilesData = response.data.data;
917
+ const serverUids = uploadedFilesData.map((f: any) => f.uid);
918
+ pollFileStatus(
919
+ serverUids,
920
+ serverStatuses => {
921
+ setFileStatuses(currentLocalStatuses => {
922
+ return currentLocalStatuses.map(localFile => {
923
+ const correspondingServerFile = serverStatuses.find(
924
+ (sf: any) => sf.uid === localFile.uid
925
+ );
926
+ if (correspondingServerFile) {
927
+ switch (correspondingServerFile.status) {
928
+ case 3:
929
+ return { ...localFile, status: 'done' };
930
+ case 4:
931
+ return { ...localFile, status: 'error' };
932
+ case 1:
933
+ case 2:
934
+ default:
935
+ return { ...localFile, status: 'parsing' };
936
+ }
937
+ }
938
+ return localFile;
939
+ });
940
+ });
941
+ // === 新增:全部完成时清空 sender 区 ===
942
+ const allDone =
943
+ serverStatuses.length > 0 &&
944
+ serverStatuses.every((file: any) => file.status === 3);
945
+ if (allDone) {
946
+ setFileStatuses([]);
947
+ // 上传完成后合并新文件到 uploadedFiles,保证结构统一
948
+ const map = new Map();
949
+ (serverStatuses || []).forEach((item: any) => {
950
+ map.set(item.id, item);
951
+ });
952
+ setFileManagerData(prev => {
953
+ const r = mergeFiles(prev.uploadedFiles, uploadedFilesData);
954
+
955
+ return {
956
+ uploadedFiles: r.map(item => {
957
+ return {
958
+ ...item,
959
+ pdfPages: map.get(item.id)?.pdfPages || 0, // 保留后端返回的 pdfPages 字段
960
+ };
961
+ }),
962
+ };
963
+ });
964
+ }
965
+ },
966
+ // onComplete: 全部解析成功后才发起AI请求
967
+ () => {
968
+ clearFn?.();
969
+ setAppStatus(pre => ({ ...pre, sender: 'processing', display: 'processing' }));
970
+ // === 新增:回填 filePath 到 fileStatuses ===
971
+ setFileStatuses(prev => {
972
+ return prev.map(localFile => {
973
+ const serverFile = uploadedFilesData.find(
974
+ (f: any) => f.uid === localFile.uid
975
+ );
976
+
977
+ if (serverFile) {
978
+ return {
979
+ ...localFile,
980
+ ...serverFile, // 合并后端所有字段
981
+ url: serverFile.filePath, // 兼容 url 字段
982
+ filePath: serverFile.filePath,
983
+ name: serverFile.fileName || localFile.name,
984
+ convertedFilePath:
985
+ serverFile.convertedFilePath || localFile.convertedFilePath,
986
+ };
987
+ }
988
+ return localFile;
989
+ });
990
+ });
991
+ // === 同步 currentFiles 字段到 chatData ===
992
+ setChatData(chatList => {
993
+ if (!chatList[0]) return chatList;
994
+ const { messages } = chatList[0];
995
+ for (let i = messages.length - 1; i >= 0; i--) {
996
+ if (messages[i].istype === 'user') {
997
+ const oldFiles = messages[i].currentFiles || [];
998
+ messages[i].currentFiles = oldFiles.map((localFile: any) => {
999
+ const serverFile = uploadedFilesData.find(
1000
+ (f: any) => f.uid === localFile.uid
1001
+ );
1002
+ return serverFile ? { ...localFile, ...serverFile } : localFile;
1003
+ });
1004
+ break;
1005
+ }
1006
+ }
1007
+ return [...chatList];
1008
+ });
1009
+ // === 发起AI请求 ===
1010
+ const now = Date.now();
1011
+ api_startChat_re(
1012
+ {
1013
+ configId: configIdToUse,
1014
+ content: data.content || data.text || '',
1015
+ fileUids: serverUids,
1016
+ lastDate: now,
1017
+ name: label,
1018
+ queryId: queryId,
1019
+ sessionId: String(sessionId || ''),
1020
+ type: 'text',
1021
+ audioUrl: '',
1022
+ enableThinking: !!senderSwitchValues.reasoning,
1023
+ enableWebsearch: !!senderSwitchValues.netSearch,
1024
+ qaChannel: senderSwitchValues.qaChannel,
1025
+ } as any,
1026
+ gmtModified => {
1027
+ fetchRecommendQuestions(queryId);
1028
+ }
1029
+ );
1030
+ },
1031
+ (errorMsg: string) => {
1032
+ setAppStatus(pre => ({ ...pre, display: 'ready', sender: 'ready' }));
1033
+ setFileStatuses(prev =>
1034
+ prev.map(f => ({ ...f, status: 'error', message: errorMsg }))
1035
+ );
1036
+ }
1037
+ );
1038
+ } else {
1039
+ const errorMsg = response.data.errorMsg || '文件上传失败';
1040
+ setAppStatus(pre => ({ ...pre, display: 'error', sender: 'ready' }));
1041
+ setFileStatuses(prev =>
1042
+ prev.map(f => ({ ...f, status: 'error', message: errorMsg }))
1043
+ );
1044
+ }
1045
+ })
1046
+ .catch(error => {
1047
+ // 检查是否为文件大小超限错误 (413)
1048
+ if (error?.response?.status === 413) {
1049
+ if (error?.response?.statusText.includes('Request Entity Too Large')) {
1050
+ message.error('文件超过最大可上传限制');
1051
+ }
1052
+ } else {
1053
+ // 其他错误显示通用网络错误提示
1054
+ message.error('网络错误');
1055
+ }
1056
+
1057
+ setAppStatus(pre => ({ ...pre, display: 'error', sender: 'ready' }));
1058
+ setFileStatuses(prev =>
1059
+ prev.map(f => ({ ...f, status: 'error', message: '网络错误' }))
1060
+ );
1061
+ });
1062
+ return;
1063
+ }
1064
+ // 3. 无文件,直接发起AI请求
1065
+ setAppStatus(pre => ({ ...pre, display: 'processing', sender: 'processing' }));
1066
+ const now = Date.now();
1067
+ setConversationList(list =>
1068
+ list.map(c => (c.sessionId === sessionId ? { ...c, gmtModified: now } : c))
1069
+ );
1070
+ clearFn?.();
1071
+ api_startChat_re(
1072
+ {
1073
+ configId: configIdToUse,
1074
+ content: data.content || data.text || '',
1075
+ fileUids: [],
1076
+ lastDate: now,
1077
+ name: label,
1078
+ queryId: queryId,
1079
+ sessionId: String(sessionId || ''),
1080
+ type: 'text',
1081
+ audioUrl: '',
1082
+ enableThinking: !!senderSwitchValues.reasoning,
1083
+ enableWebsearch: !!senderSwitchValues.netSearch,
1084
+ qaChannel: senderSwitchValues.qaChannel,
1085
+ } as any,
1086
+ gmtModified => {
1087
+ fetchRecommendQuestions(queryId);
1088
+ }
1089
+ );
1090
+ }
1091
+
1092
+ // =================================================================
1093
+ // Event Handlers - 事件处理器
1094
+ // =================================================================
1095
+
1096
+ /**
1097
+ * 处理点赞反馈
1098
+ */
1099
+ function handleThumbsup(data: any) {
1100
+ if (data.restult == 1) {
1101
+ handleCancelFeedBack({ url, token, CSRFToken }, { queryId: data.queryId }, () => {
1102
+ setFeed(data.queryId, null);
1103
+ });
1104
+ } else {
1105
+ handleFeedBack({ url, token, CSRFToken }, { restult: 1, queryId: data.queryId }, () => {
1106
+ setFeed(data.queryId, 1);
1107
+ });
1108
+ }
1109
+ }
1110
+
1111
+ /**
1112
+ * 处理新建一个临时会话
1113
+ */
1114
+ function handleConversationCreate(data: any) {
1115
+ const sessionId = uid(32);
1116
+ // 新会话对象,等待用户输入后再发起创建请求
1117
+ const newConversation = {
1118
+ sessionId,
1119
+ label: '未命名会话',
1120
+ gmtModified: Date.now(),
1121
+ searchConfigDTO: {},
1122
+ filePath: '',
1123
+ configId: '',
1124
+ isNew: true, // 标记为新会话
1125
+ };
1126
+ // 在列表顶部插入新会话
1127
+ setConversationList(list => [{ ...newConversation, messages: [] }, ...list]);
1128
+ setChatData(list => [{ id: sessionId, messages: [], ...newConversation }, ...list]);
1129
+ // 激活新会话
1130
+ setActiveSessionId(sessionId);
1131
+ }
1132
+
1133
+ /**
1134
+ * 文件管理器:处理文件选择后的上传
1135
+ */
1136
+ const handleFileManagerUpload = (fileList: FileList) => {
1137
+ // 1. 收集所有已经存在的文件名
1138
+ const existingFileNames = new Set<string>([
1139
+ ...fileManagerData.uploadedFiles.map((f: any) => f.file.name as string),
1140
+ ...pendingFiles.map((f: any) => f.file.name as string),
1141
+ ]);
1142
+
1143
+ const newFiles: { file: File; type: string; status: string; preview?: string }[] = [];
1144
+ const duplicateFileNames = new Set<string>();
1145
+
1146
+ // 2. 遍历新选择的文件,筛选出重复项和有效项
1147
+ Array.from(fileList).forEach(file => {
1148
+ if (existingFileNames.has(file.name)) {
1149
+ duplicateFileNames.add(file.name);
1150
+ } else {
1151
+ newFiles.push({
1152
+ file,
1153
+ type: getFileTypeByName(file.name),
1154
+ status: 'pending',
1155
+ preview: file.type.startsWith('image') ? URL.createObjectURL(file) : undefined,
1156
+ });
1157
+ existingFileNames.add(file.name); // 添加到Set中,以防本次上传的文件之间有重复
1158
+ }
1159
+ });
1160
+
1161
+ // 3. 如果有重复,发出警告
1162
+ if (duplicateFileNames.size > 0) {
1163
+ message.warning(`不能上传同名文件: ${Array.from(duplicateFileNames).join(', ')}`);
1164
+ }
1165
+
1166
+ // 4. 将有效的文件添加到待上传列表
1167
+ if (newFiles.length > 0) {
1168
+ setPendingFiles(prev => [...prev, ...newFiles]);
1169
+ // 直接同步到核心发送器组件
1170
+ setUploadedFilesRef.current?.(newFiles);
1171
+ }
1172
+ };
1173
+
1174
+ /**
1175
+ * 文件管理器:处理文件移除
1176
+ */
1177
+ const handleFileManagerRemove = (fileObj: any, idx: number, _type: string) => {
1178
+ removeFileRef.current?.(fileObj, idx, _type);
1179
+ };
1180
+
1181
+ /**
1182
+ * 处理会话列表项点击
1183
+ */
1184
+ function handleItemClick(data: any) {
1185
+ if (data?.sessionId && data.sessionId !== activeSessionId) {
1186
+ setActiveSessionId(data.sessionId);
1187
+ setAppStatus(prev => ({ ...prev, sender: 'ready' }));
1188
+ // 点击会话时,使用其最后一条AI消息的推荐问题
1189
+ const conv = chatData.find(c => c.id === data.sessionId);
1190
+ if (conv && conv.messages && conv.messages.length > 0) {
1191
+ const lastAiMsg = [...conv.messages].reverse().find((m: any) => m.istype === 'ai');
1192
+ if (lastAiMsg && lastAiMsg.recommendQuestion) {
1193
+ setRecommandQuestions(lastAiMsg.recommendQuestion);
1194
+ } else {
1195
+ setRecommandQuestions([]);
1196
+ }
1197
+ } else {
1198
+ setRecommandQuestions([]);
1199
+ }
1200
+ }
1201
+ }
1202
+
1203
+ // =================================================================
1204
+ // Async Data Operations - 异步数据操作 (供事件发射器使用)
1205
+ // =================================================================
1206
+
1207
+ /**
1208
+ * 异步删除会话接口
1209
+ */
1210
+ const async_deleteConversation = async (sessionId: string) => {
1211
+ return new Promise((resolve, reject) => {
1212
+ handleDeleteConversation({ url, token, CSRFToken }, { sessionId }, (res: any) => {
1213
+ if (res) {
1214
+ message.success('删除成功!');
1215
+ resolve(res);
1216
+ } else {
1217
+ message.error('删除失败!');
1218
+ reject(res);
1219
+ }
1220
+ });
1221
+ });
1222
+ };
1223
+ /**
1224
+ * 异步刷新会话列表接口
1225
+ */
1226
+ const async_refreshConversationList = async () => {
1227
+ try {
1228
+ const convRes = await axios.get(
1229
+ `${url}/qa/dialogue/list?pageNo=1&pageSize=3000&sysType=INT_SESSION`,
1230
+ {
1231
+ headers: { Authorization: token },
1232
+ }
1233
+ );
1234
+ const rawList = convRes.data.data.records || [];
1235
+ setConversationList(rawList);
1236
+ return rawList;
1237
+ } catch (e) {
1238
+ message.error('刷新会话列表失败');
1239
+ throw e;
1240
+ }
1241
+ };
1242
+
1243
+ /**
1244
+ * 异步重命名会话接口
1245
+ */
1246
+ const async_renameConversation = async (sessionId: string, newLabel: string) => {
1247
+ return new Promise((resolve, reject) => {
1248
+ handleRename({ url, token, CSRFToken }, { label: newLabel, sessionId }, res => {
1249
+ if (res) {
1250
+ // 成功后立即更新本地状态
1251
+ const now = Date.now();
1252
+ setConversationList(list =>
1253
+ list.map(item =>
1254
+ item.sessionId === sessionId ? { ...item, label: newLabel, gmtModified: now } : item
1255
+ )
1256
+ );
1257
+ message.success('重命名成功!');
1258
+ resolve(res);
1259
+ } else {
1260
+ message.error('重命名失败');
1261
+ reject(res);
1262
+ }
1263
+ });
1264
+ });
1265
+ };
1266
+
1267
+ // =================================================================
1268
+ // Local State Updaters - 本地状态更新 (供事件发射器使用)
1269
+ // =================================================================
1270
+
1271
+ /**
1272
+ * 从本地状态中移除会话项
1273
+ */
1274
+ const fn_removeItemFromList = (sessionId: string) => {
1275
+ setConversationList(list => {
1276
+ const newList = list.filter(c => c.sessionId !== sessionId);
1277
+ // 如果删除的是当前会话,则激活列表中的第一个
1278
+ if (activeSessionId === sessionId) {
1279
+ if (newList.length > 0) {
1280
+ setActiveSessionId(newList[0].sessionId);
1281
+ } else {
1282
+ // 如果列表空了,则新建一个
1283
+ handleConversationCreate({});
1284
+ }
1285
+ }
1286
+ return newList;
1287
+ });
1288
+ };
1289
+
1290
+ /**
1291
+ * 在本地状态中重命名会话项
1292
+ */
1293
+ const fn_renameItemInList = (sessionId: string, newLabel: string) => {
1294
+ const now = Date.now();
1295
+ setConversationList(list =>
1296
+ list.map(item =>
1297
+ item.sessionId === sessionId ? { ...item, label: newLabel, gmtModified: now } : item
1298
+ )
1299
+ );
1300
+ };
1301
+
1302
+ const handleFeedDownConfirm = (data: any) => {
1303
+ handleFeedBack({ url, token, CSRFToken }, { ...data, ...feedParam, restult: 0 }, () => {
1304
+ setFeed(feedParam.queryId, 0);
1305
+ });
1306
+ };
1307
+
1308
+ const editDialog = (data: any) => {
1309
+ const newLabel = data.new_name;
1310
+ const oldLabel = curEditData.data.label;
1311
+ if (newLabel && newLabel !== oldLabel) {
1312
+ curEditData
1313
+ .async_renameConversation(newLabel)
1314
+ .then(() => {
1315
+ curEditData.fn_renameItemInList(newLabel);
1316
+ setisRenameModalOpen(false);
1317
+ })
1318
+ .catch(() => {
1319
+ // 重命名失败
1320
+ });
1321
+ } else {
1322
+ setisRenameModalOpen(false);
1323
+ }
1324
+ };
1325
+
1326
+ const editRemove = (data: any) => {
1327
+ curEditData
1328
+ .async_deleteConversation()
1329
+ .then(() => {
1330
+ curEditData.fn_removeItemFromList();
1331
+ setisRemoveModalOpen(false);
1332
+ })
1333
+ .catch(() => {
1334
+ // 删除失败
1335
+ });
1336
+ };
1337
+
1338
+ const handleEvent = (type: string, data?: any) => {
1339
+ console.log(type, data);
1340
+ switch (type) {
1341
+ case 'close_remove':
1342
+ setisRemoveModalOpen(false);
1343
+ break;
1344
+ case 'close_rename':
1345
+ setisRenameModalOpen(false);
1346
+ break;
1347
+ case 'ok_rename':
1348
+ editDialog(data);
1349
+ break;
1350
+ case 'ok_remove':
1351
+ editRemove(data);
1352
+ break;
1353
+ }
1354
+ };
1355
+
1356
+ // =================================================================
1357
+ // Event Emitter - 事件分发器
1358
+ // =================================================================
1359
+
1360
+ const getDataBaseData = async ({ url, params, replacePredataFunction }: any) => {
1361
+ try {
1362
+ const res = await axios.post(url, params, {
1363
+ headers: { Authorization: token },
1364
+ });
1365
+ const data = res?.data?.data?.data;
1366
+ if (data) {
1367
+ replacePredataFunction(data);
1368
+ }
1369
+ } catch (error) {
1370
+ console.log('error', error);
1371
+ }
1372
+ };
1373
+ /**
1374
+ * 统一事件分发器,连接业务逻辑与所有子组件的事件
1375
+ * @param {string} eventName - 事件名称
1376
+ * @param {any} data - 事件数据
1377
+ */
1378
+ const mergedEventsEmit = (eventName: string, data: any) => {
1379
+ console.log(`[Event] ${eventName}`, data);
1380
+ switch (eventName) {
1381
+ case 'retrieve-sql-data:pagingation_click':
1382
+ const { page, pageSize, fetchUrl, source, replacePredataFunction } = data;
1383
+ getDataBaseData({
1384
+ url: `${url}${fetchUrl}`,
1385
+ replacePredataFunction,
1386
+ params: { pageNo: page, pageSize, convertedFilePath: source, stream: false },
1387
+ });
1388
+ break;
1389
+ case 'web_referenceFile_title:click':
1390
+ if (!data?.data?.link) return console.error('没有网页地址');
1391
+ window.open(data.data.link, '_blank');
1392
+ break;
1393
+ // --- 对话列表事件 ---
1394
+ case 'conversation:item_click':
1395
+ handleItemClick(data);
1396
+ break;
1397
+
1398
+ case 'files:finished_animation_complete':
1399
+ setFileStatuses([]);
1400
+ break;
1401
+
1402
+ case 'conversation:create':
1403
+ handleConversationCreate(data);
1404
+ break;
1405
+ case 'conversation:new_assistant_change':
1406
+ setNewAssistantId(data.assistantId);
1407
+ break;
1408
+ case 'conversations:rename_icon_clicked':
1409
+ setCurEditData({
1410
+ data,
1411
+ async_renameConversation: (newLabel: string) =>
1412
+ async_renameConversation(data.sessionId, newLabel),
1413
+ fn_renameItemInList: (newLabel: string) =>
1414
+ fn_renameItemInList(data.sessionId, newLabel),
1415
+ });
1416
+ setisRenameModalOpen(true);
1417
+ break;
1418
+ case 'conversations:delete_icon_clicked':
1419
+ setCurEditData({
1420
+ data,
1421
+ async_deleteConversation: () => async_deleteConversation(data.sessionId),
1422
+ async_refreshConversationList,
1423
+ fn_removeItemFromList: () => fn_removeItemFromList(data.sessionId),
1424
+ });
1425
+ setisRemoveModalOpen(true);
1426
+ break;
1427
+ case 'sender:send':
1428
+ handleSenderSend(data, data.clearFn);
1429
+ break;
1430
+ case 'sender:send_recommandQuestion': {
1431
+ // 旧链路:来自 Sender 的推荐问题,可能已经在 AiChat 内转发为 sender:send
1432
+ // 这里兜底处理,确保能直接触发发送
1433
+ const content = (data?.content ?? '').toString();
1434
+ if (!content) break;
1435
+ handleSenderSend({ content }, () => {});
1436
+ break;
1437
+ }
1438
+ case 'chatbox:follow_up_question_click': {
1439
+ // 新链路:来自 ChatMessageAdapter 的追问点击
1440
+ const content = (data?.question ?? data?.content ?? '').toString();
1441
+ if (!content) break;
1442
+ handleSenderSend({ content }, () => {});
1443
+ break;
1444
+ }
1445
+ case 'sender:action_history':
1446
+ setFileManagerOpen(true);
1447
+ break;
1448
+ case 'sender:stop':
1449
+ stoppedRef.current = true;
1450
+ if (uploadController) {
1451
+ uploadController.abort();
1452
+ uploadController = null;
1453
+ }
1454
+ if (stopPoll) {
1455
+ stopPoll();
1456
+ stopPoll = null;
1457
+ }
1458
+ setAppStatus(pre => ({ ...pre, display: 'ready', sender: 'ready' }));
1459
+ setFileStatuses([]);
1460
+ stopStream?.();
1461
+ break;
1462
+ case 'sender:clear':
1463
+ data.clearFn?.();
1464
+ break;
1465
+ case 'sender:configChange':
1466
+ // 配置项变化,记录最新 switch 状态
1467
+ if (data && data.all) {
1468
+ setSenderSwitchValues(data.all);
1469
+ }
1470
+ break;
1471
+
1472
+ // --- 消息卡片事件 ---
1473
+ case 'action_copy:click':
1474
+ if (data) {
1475
+ toCopy(data.content, data.event);
1476
+ }
1477
+ break;
1478
+ case 'chatbox:copy': {
1479
+ const text = data?.content || '';
1480
+ if (!text) break;
1481
+ // 直接使用 Clipboard API,避免依赖 event.currentTarget
1482
+ const copyByClipboardApi = async (t: string) => {
1483
+ try {
1484
+ await navigator.clipboard.writeText(t);
1485
+ message.success('已复制到剪贴板');
1486
+ return true;
1487
+ } catch (_) {
1488
+ return false;
1489
+ }
1490
+ };
1491
+ const fallbackCopy = (t: string) => {
1492
+ const textarea = document.createElement('textarea');
1493
+ textarea.value = t;
1494
+ textarea.style.position = 'fixed';
1495
+ textarea.style.opacity = '0';
1496
+ document.body.appendChild(textarea);
1497
+ textarea.focus();
1498
+ textarea.select();
1499
+ try {
1500
+ document.execCommand('copy');
1501
+ message.success('已复制到剪贴板');
1502
+ } catch (_) {
1503
+ message.error('复制失败');
1504
+ }
1505
+ document.body.removeChild(textarea);
1506
+ };
1507
+ copyByClipboardApi(text).then(ok => {
1508
+ if (!ok) fallbackCopy(text);
1509
+ });
1510
+ break;
1511
+ }
1512
+ case 'chatbox:repeat':
1513
+ console.log(data, 'sdkflskdjflksdjfklsdjlfkjsdklfjsdklfjlks');
1514
+ handleReSenderSend(data);
1515
+ break;
1516
+ case 'action_thumbsup':
1517
+ handleThumbsup(data);
1518
+ break;
1519
+ case 'chatbox:like':
1520
+ handleThumbsup({ queryId: data?.queryId, restult: data?.result });
1521
+ break;
1522
+ case 'action_thumbsdown':
1523
+ if (data.restult === 0) {
1524
+ handleCancelFeedBack({ url, token, CSRFToken }, { queryId: data.queryId }, () => {
1525
+ setFeed(data.queryId, null);
1526
+ });
1527
+ } else {
1528
+ setFeedParam(data);
1529
+ setOpenFeed(true);
1530
+ }
1531
+ break;
1532
+ case 'chatbox:dislike':
1533
+ if (data?.result === 0) {
1534
+ handleCancelFeedBack({ url, token, CSRFToken }, { queryId: data.queryId }, () => {
1535
+ setFeed(data.queryId, null);
1536
+ });
1537
+ } else {
1538
+ setFeedParam({ queryId: data?.queryId });
1539
+ setOpenFeed(true);
1540
+ }
1541
+ break;
1542
+ case 'action_retrive_tag':
1543
+ // 处理 retrive-tag 点击事件,透传给父组件
1544
+ eventsEmit?.('action_retrive_tag', data);
1545
+ break;
1546
+ case 'referenceFile_view':
1547
+ // 处理文件预览事件,透传给父组件
1548
+ eventsEmit?.('referenceFile_view', data);
1549
+ break;
1550
+ case 'referenceFile_question':
1551
+ // 处理单文档对话事件,透传给父组件
1552
+ console.log(data, 'zhezhhzhzh');
1553
+ eventsEmit?.('referenceFile_question', data);
1554
+ break;
1555
+ case 'referenceFile_download':
1556
+ // 处理文件下载事件,透传给父组件
1557
+ eventsEmit?.('referenceFile_download', data);
1558
+ break;
1559
+ case 'retrive_tag:click':
1560
+ // 处理 retrive-tag 点击事件,透传给父组件
1561
+ eventsEmit?.('retrive_tag:click', data);
1562
+ break;
1563
+ // --- 文件管理器事件 ---
1564
+ case 'fileManager:change': {
1565
+ const files = Array.isArray(data?.uploadedFiles) ? data.uploadedFiles : [];
1566
+ setPendingFiles(files);
1567
+ // 保存核心组件传递的文件处理函数引用
1568
+ if (typeof data?.setUploadedFiles === 'function') {
1569
+ setUploadedFilesRef.current = data.setUploadedFiles;
1570
+ removeFileRef.current = data.removeFile;
1571
+ }
1572
+ break;
1573
+ }
1574
+
1575
+ // --- 文件管理器上传文件删除事件,透传到业务层 ---
1576
+ case 'uploaded_file:removeFromTemp': {
1577
+ console.log('[DEBUG] mergedEventsEmit uploaded_file:removeFromTemp 事件', data);
1578
+
1579
+ const uid = data?.file?.uid;
1580
+ if (!uid) {
1581
+ console.warn('[DEBUG] 缺少uid,无法删除临时文件', data);
1582
+ break;
1583
+ }
1584
+ // 获取当前对话最后一条 AI 消息的 queryId
1585
+ const lastAiMsg = [...(currentChatData.messages || [])]
1586
+ .reverse()
1587
+ .find((msg: any) => msg.istype === 'user');
1588
+ const queryId = lastAiMsg?.queryId;
1589
+
1590
+ removeFilefromTempUpload({ url, token, CSRFToken }, { uid, queryId }, (success, res) => {
1591
+ if (success) {
1592
+ message.success('文件已从临时知识库移除');
1593
+ // 用后端返回的新data数组(新文件列表)更新本地uploadedFiles
1594
+ //console.log('[DEBUG] removeFilefromTempUpload 回调 true,res:', res, Array.isArray(res.data && res.data.data));
1595
+ if (res && Array.isArray(res.data && res.data.data)) {
1596
+ const uploaded = res.data.data.map((f: any) => {
1597
+ const fileName = f.name || f.fileName || '';
1598
+ return {
1599
+ file: {
1600
+ name: fileName,
1601
+ size: Number(f.size?.replace('MB', '')) * 1024 * 1024 || 0,
1602
+ type: f.type,
1603
+ } as File,
1604
+ type: getFileTypeByName(fileName),
1605
+ status: 'uploaded' as const,
1606
+ url: f.filePath,
1607
+ convertedFilePath: f.convertedFilePath,
1608
+ uid: f.uid,
1609
+ preview: f.type === 'image' ? f.filePath : undefined,
1610
+ };
1611
+ });
1612
+ // console.log('[DEBUG] uploaded 构造完成:', uploaded, '当前 fileManagerData:', fileManagerData);
1613
+ setFileManagerData({ uploadedFiles: uploaded });
1614
+ } else {
1615
+ }
1616
+ } else {
1617
+ console.log('[DEBUG] removeFilefromTempUpload 回调 false,res:', res);
1618
+ message.error('文件移除失败');
1619
+ }
1620
+ });
1621
+ break;
1622
+ }
1623
+ case 'uploaded_file:remove_batch':
1624
+ break;
1625
+
1626
+ default:
1627
+ // 其他未处理事件可继续向上层抛出
1628
+ eventsEmit?.(eventName, data);
1629
+ }
1630
+ };
1631
+
1632
+ // =================================================================
1633
+ // Component & UI Configuration - 组件及UI配置
1634
+ // =================================================================
1635
+
1636
+ /**
1637
+ * Sidebar 配置
1638
+ */
1639
+ const sidebar: SidebarTabConfig[] = [
1640
+ {
1641
+ key: 'conversations',
1642
+ type: 'custom',
1643
+ label: '对话历史',
1644
+ icon: <History size={16} />,
1645
+ enabled: true,
1646
+ component: (
1647
+ <GientechConversationPanel
1648
+ data={conversationList}
1649
+ assistantList={assistantList}
1650
+ activedItem={activeSessionId || ''}
1651
+ sortRules={[
1652
+ { label: '当天', dayPeriod: 1 },
1653
+ { label: '3天', dayPeriod: 3 },
1654
+ { label: '7天', dayPeriod: 7 },
1655
+ { label: '30天', dayPeriod: 30 },
1656
+ ]}
1657
+ eventsEmit={mergedEventsEmit}
1658
+ styles={normalizedStyles.theme}
1659
+ />
1660
+ ),
1661
+ },
1662
+ ];
1663
+
1664
+ /**
1665
+ * 业务层自定义组件
1666
+ */
1667
+ const WelcomeComponent = React.useMemo(() => {
1668
+ return () => (
1669
+ <GientechNewChatWelcome
1670
+ eventsEmit={mergedEventsEmit}
1671
+ assistantList={assistantList}
1672
+ styles={normalizedStyles}
1673
+ selectedId={selectedAssistantId}
1674
+ onSelectAssistant={setSelectedAssistantId}
1675
+ />
1676
+ );
1677
+ }, [selectedAssistantId]);
1678
+
1679
+ const businessCustomComponents = React.useMemo(
1680
+ () => ({
1681
+ AiChatBox: (msg: any, idx: number) => {
1682
+ const isLastMessage = idx === (currentChatData?.messages?.length || 0) - 1;
1683
+
1684
+ // 根据消息的 status 和 appStatus 来确定 displayStatus
1685
+ let displayStatus = 'ready';
1686
+ if (isLastMessage) {
1687
+ // 优先使用消息的 status 字段
1688
+ if (msg.status !== undefined) {
1689
+ switch (msg.status) {
1690
+ case 0: // StatusType.Pending
1691
+ displayStatus = 'thinking';
1692
+ break;
1693
+ case 1: // StatusType.Process
1694
+ displayStatus = 'processing';
1695
+ break;
1696
+ case 2: // StatusType.Done
1697
+ displayStatus = 'ready';
1698
+ break;
1699
+ case 3: // StatusType.Error
1700
+ displayStatus = 'error';
1701
+ break;
1702
+ default:
1703
+ displayStatus = appStatus.display;
1704
+ }
1705
+ } else {
1706
+ // 如果没有 status 字段,使用 appStatus.display
1707
+ displayStatus = appStatus.display;
1708
+ }
1709
+ }
1710
+ // 将推荐问题强制转换为数组,避免切换会话时报 .map 错误
1711
+ let safeFollowUps: string[] = [];
1712
+ if (Array.isArray(recommandQuestions)) {
1713
+ safeFollowUps = recommandQuestions as string[];
1714
+ } else if (typeof recommandQuestions === 'string') {
1715
+ try {
1716
+ safeFollowUps = JSON.parse(recommandQuestions) || [];
1717
+ } catch {
1718
+ safeFollowUps = [];
1719
+ }
1720
+ }
1721
+ // 保证传入 ChatMessageAdapter 的 reference/webReference/graphReference 始终是有效的 JSON 字符串
1722
+ const safeReference = (() => {
1723
+ const r = (msg as any)?.reference;
1724
+ if (!r) return '[]';
1725
+ if (typeof r === 'string') {
1726
+ try {
1727
+ JSON.parse(r);
1728
+ return r;
1729
+ } catch {
1730
+ return '[]';
1731
+ }
1732
+ }
1733
+ try {
1734
+ return JSON.stringify(r);
1735
+ } catch {
1736
+ return '[]';
1737
+ }
1738
+ })();
1739
+ const safeWebReference = (() => {
1740
+ const r = (msg as any)?.webReference;
1741
+ if (!r) return '[]';
1742
+ if (typeof r === 'string') {
1743
+ try {
1744
+ JSON.parse(r);
1745
+ return r;
1746
+ } catch {
1747
+ return '[]';
1748
+ }
1749
+ }
1750
+ try {
1751
+ return JSON.stringify(r);
1752
+ } catch {
1753
+ return '[]';
1754
+ }
1755
+ })();
1756
+ const safeGraphReference = (() => {
1757
+ const r = (msg as any)?.graphReference;
1758
+ if (!r) return '[]';
1759
+ if (typeof r === 'string') {
1760
+ try {
1761
+ JSON.parse(r);
1762
+ return r;
1763
+ } catch {
1764
+ return '[]';
1765
+ }
1766
+ }
1767
+ try {
1768
+ return JSON.stringify(r);
1769
+ } catch {
1770
+ return '[]';
1771
+ }
1772
+ })();
1773
+ return (
1774
+ <ChatMessageAdapter
1775
+ key={`${msg?.queryId || msg?.id || idx}-${msg?.istype || 'unknown'}`}
1776
+ {...msg}
1777
+ reference={safeReference}
1778
+ webReference={safeWebReference}
1779
+ graphReference={safeGraphReference}
1780
+ contentType="stream" // 添加 contentType 确保 slateContent 能正确生成
1781
+ displayStatus={displayStatus}
1782
+ eventsEmit={mergedEventsEmit}
1783
+ // 仅最后一条 AI 消息展示追问
1784
+ isLast={isLastMessage && msg?.istype === 'ai'}
1785
+ followUpQuestions={isLastMessage && msg?.istype === 'ai' ? safeFollowUps : []}
1786
+ defaultAnswer={
1787
+ currentChatData?.searchConfigDTO?.defaultAnswer ||
1788
+ '不知道什么原因我们没能查到你要的问题*-*'
1789
+ }
1790
+ is_download={is_download}
1791
+ styles={normalizedStyles as any}
1792
+ />
1793
+ );
1794
+ },
1795
+ LogoBox: (props: any) => {
1796
+ if (props && props.LogoBox === null) return null;
1797
+ if (props && typeof props.LogoBox === 'function') return <props.LogoBox />;
1798
+ return <div className="text-2xl w-full text-center font-bold">小鲸智能助手</div>;
1799
+ },
1800
+ DisplayLoading: () => <DisplayLoading />,
1801
+ DisplayError: () => <DisplayError />,
1802
+ AppError: () => (
1803
+ <AppError
1804
+ msg="应用初始化失败"
1805
+ subMsg="无法完成应用初始化,这可能是由于网络连接问题或服务配置错误导致的。请检查您的网络连接并重试。"
1806
+ />
1807
+ ),
1808
+ AppLoading: () => <AppLoading />,
1809
+ UserChatBox: (msg: any, idx: number) => {
1810
+ return (
1811
+ <ChatMessageAdapter
1812
+ key={`${msg?.queryId || msg?.id || idx}-user`}
1813
+ {...msg}
1814
+ contentType="plainText"
1815
+ isUser={true}
1816
+ displayStatus="ready"
1817
+ eventsEmit={mergedEventsEmit}
1818
+ styles={normalizedStyles as any}
1819
+ fileManagerData={fileManagerData}
1820
+ />
1821
+ );
1822
+ },
1823
+ WelcomeComponent,
1824
+ }),
1825
+ [
1826
+ WelcomeComponent,
1827
+ currentChatData,
1828
+ appStatus.display,
1829
+ mergedEventsEmit,
1830
+ normalizedStyles,
1831
+ is_download,
1832
+ fileManagerData,
1833
+ ]
1834
+ );
1835
+
1836
+ /**
1837
+ * 合并外部传入和业务默认的自定义组件,外部优先
1838
+ */
1839
+ const mergedCustomComponents: { [key: string]: any } = {
1840
+ ...businessCustomComponents,
1841
+ ...(rest.CustomComponents || {}),
1842
+ };
1843
+
1844
+ useEffect(() => {
1845
+ setDynamicSenderConfig((prev: any) => {
1846
+ const switchs = Array.isArray(prev.switchs)
1847
+ ? prev.switchs.map((item: any) =>
1848
+ item.name === 'reasoning' ? { ...item, enabled: is_enableThinking } : item
1849
+ )
1850
+ : [];
1851
+ return {
1852
+ ...prev,
1853
+ switchs,
1854
+ };
1855
+ });
1856
+ }, [is_enableThinking]);
1857
+ // =================================================================
1858
+ // Render - 渲染
1859
+ // =================================================================
1860
+ return (
1861
+ <>
1862
+ <FileManager
1863
+ open={fileManagerOpen}
1864
+ onCancel={() => setFileManagerOpen(false)}
1865
+ files={fileManagerData.uploadedFiles}
1866
+ currentFiles={pendingFiles}
1867
+ onUpload={handleFileManagerUpload}
1868
+ onRemoveFile={handleFileManagerRemove}
1869
+ styles={normalizedStyles}
1870
+ eventsEmit={mergedEventsEmit}
1871
+ />
1872
+
1873
+ <WrappedComponent
1874
+ {...rest}
1875
+ status={appStatus}
1876
+ chatData={currentChatData}
1877
+ sidebar={sidebar}
1878
+ recommandQuestions={[]}
1879
+ eventsEmit={mergedEventsEmit}
1880
+ styles={normalizedStyles as any}
1881
+ senderConfig={dynamicSenderConfig}
1882
+ fileUploadStatus={fileStatuses}
1883
+ CustomComponents={mergedCustomComponents}
1884
+ activeSessionId={activeSessionId}
1885
+ // scrollOld={true}
1886
+ />
1887
+ <FeedBackModal
1888
+ open={openFeed}
1889
+ feedBackList={feedBackList}
1890
+ setOpen={setOpenFeed}
1891
+ handleConfirm={handleFeedDownConfirm}
1892
+ />
1893
+ <RenameModal
1894
+ isModalOpen={isRenameModalOpen}
1895
+ handleOk={handleEvent}
1896
+ handleCancel={() => handleEvent('close_rename')}
1897
+ data={curEditData?.data || {}}
1898
+ />
1899
+ <DeleteModal
1900
+ isModalOpen={isRemoveModalOpen}
1901
+ handleOk={handleEvent}
1902
+ handleCancel={() => handleEvent('close_remove')}
1903
+ data={curEditData?.data || {}}
1904
+ />
1905
+ </>
1906
+ );
1907
+ };
1908
+ }