ConversationHistoryContainer.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. <template>
  2. <div class="conversation-history-container">
  3. <div class="conversation-history-left"></div>
  4. <div class="conversation-history-main">
  5. <HistoryName />
  6. <HistorySearchInput @search="handleHistorySearch">
  7. <HistoryTypeDropdown @typeSelected="handleTypeSelected" :list_status="api_status" />
  8. </HistorySearchInput>
  9. <HistoryList
  10. v-if="list_history.length !== 0"
  11. :list="list_history"
  12. :can_loadmore="can_loadmore"
  13. :agent_icon="agent_icon"
  14. @loadmore="handleListLoadMore"
  15. @active="handleListItemActive"
  16. />
  17. <template v-if="api_status === 1 || api_status === 2">
  18. <div class="infomations-container">
  19. <a-spin :size="32" />
  20. </div>
  21. </template>
  22. <template v-if="api_status === 3 && list_history.length === 0">
  23. <div class="infomations-container">
  24. <a-empty> {{ $t('conversation.history_empty') }} </a-empty>
  25. </div>
  26. </template>
  27. <template v-if="api_status === 4">
  28. <div class="infomations-container">
  29. <a-result status="warning" :title="api_message"> </a-result>
  30. </div>
  31. </template>
  32. <template v-if="api_status === 5">
  33. <div class="infomations-container">
  34. <a-result status="error" :title="api_message"> </a-result>
  35. </div>
  36. </template>
  37. <div class="conversation-history-placeholder"></div>
  38. </div>
  39. <div class="conversation-history-right"></div>
  40. </div>
  41. </template>
  42. <script setup>
  43. import { ref, onMounted } from 'vue';
  44. import HistoryName from '../../modules/conversation-history/HistoryName.vue';
  45. import HistorySearchInput from '../../modules/conversation-history/HistorySearchInput.vue';
  46. import HistoryList from '../../modules/conversation-history/HistoryList.vue';
  47. import HistoryTypeDropdown from '../../modules/conversation-history/HistoryTypeDropdown.vue';
  48. import { apiConversationHistoryList } from '../../modules/conversation-history/api-conversation-history';
  49. import { getIconIntelligent } from '../../modules/common/icon-map';
  50. import { getTodayZeroTimestamp, ONE_DAY, ONE_MONTH } from '../../utils/datetime';
  51. const emit = defineEmits(['historyActive']);
  52. const page_no = ref(1); // 当前页码
  53. const page_count = ref(50); // 每页显示的数量
  54. const search_text = ref(''); // 搜索字符串
  55. const agent_id = ref('');
  56. // const agent_item = ref(null);
  57. const can_loadmore = ref(true); // 是否可以加载更多
  58. // api请求的状态
  59. const api_status = ref(1); // api状态 1 请求未启动 2 请求进心中 3 请求已成功 4 请求失败(网络成功,但是code不等于200) 5 请求出错
  60. const api_message = ref(''); // 请求消息 与 api_status 对应的 message 消息内容
  61. // 历史记录列表数据
  62. const list_history = ref([]); // 历史记录的列表数据
  63. const count_today = ref(0); // 记录今天的数据量
  64. const count_month = ref(0); // 记录本月的数据量
  65. const count_earlier = ref(0); // 记录更早的数据量
  66. // 智能体的图标
  67. const agent_icon = ref('');
  68. const today_zero = getTodayZeroTimestamp(); // 今天零点
  69. const yesterday_zero = today_zero - ONE_DAY; // 昨天零点
  70. const last_month_zero = yesterday_zero - ONE_MONTH; // 上个月(30天之前的)零点
  71. /**
  72. * 将数据进行分类
  73. * 依据时间戳,将列表数据分成三种类型:今天、本月、更早
  74. * 今天:从今天零点开始到现在
  75. * 本月:从今天零点之前,往前推30天
  76. * 更早:在30天之前的数据
  77. * @param res_list
  78. */
  79. function dropListByType(res_list) {
  80. res_list.forEach((item, index) => {
  81. let label_item = null; // label项目(今天、本月、更早以前)
  82. const content_item = item; // 数据项目
  83. // const create_time = content_item.updated_time; // 会话创建时间
  84. // const create_time_str = content_item.create_date; // "2024-12-18 11:45:23"
  85. /**
  86. * 在后端返回的数据中
  87. * 有些情况下,存在 create_date 字段
  88. * 另外一些情况下,存在 updated_time字段
  89. */
  90. const createTime = new Date(content_item.create_date);
  91. const createStamp = createTime.getTime();
  92. let create_stamp = null;
  93. if (isNaN(createStamp)) {
  94. // 非法的
  95. // 使用updated_time
  96. const updateTime = new Date(content_item.updated_time);
  97. const updateStamp = updateTime.getTime();
  98. if (isNaN(updateStamp)) {
  99. // 非法的: 使用此刻的数据,意味着所有的数据都是今天的
  100. create_stamp = Date.now();
  101. } else {
  102. create_stamp = updateStamp;
  103. }
  104. } else {
  105. create_stamp = createStamp;
  106. }
  107. content_item.create_time = create_stamp;
  108. if (create_stamp >= today_zero) {
  109. // 今天的数据
  110. if (count_today.value === 0) {
  111. label_item = {
  112. client_field_content_type: 'label',
  113. client_field_timestamp_type: 'today',
  114. id: 'today'
  115. };
  116. }
  117. // 非第0个数据
  118. content_item.client_field_timestamp_type = 'today'; // 客户端自定义字段: 时间类型
  119. content_item.client_field_content_type = 'content'; // 内容类别
  120. count_today.value = count_today.value + 1;
  121. } else if (create_stamp < today_zero && create_stamp > last_month_zero) {
  122. // 本月的数据
  123. if (count_month.value === 0) {
  124. label_item = {
  125. client_field_content_type: 'label',
  126. client_field_timestamp_type: 'month',
  127. id: 'month'
  128. };
  129. }
  130. content_item.client_field_timestamp_type = 'month'; // 客户端自定义字段: 时间类型
  131. content_item.client_field_content_type = 'content'; // 内容类别
  132. count_month.value = count_month.value + 1;
  133. } else {
  134. // 更早以前的数据
  135. if (count_earlier.value === 0) {
  136. label_item = {
  137. client_field_content_type: 'label',
  138. client_field_timestamp_type: 'earlier',
  139. id: 'earlier'
  140. };
  141. }
  142. content_item.client_field_timestamp_type = 'earlier'; // 客户端自定义字段: 时间类型
  143. content_item.client_field_content_type = 'content'; // 内容类别
  144. count_earlier.value = count_earlier.value + 1;
  145. }
  146. if (label_item) {
  147. list_history.value.push(label_item);
  148. }
  149. list_history.value.push(content_item);
  150. });
  151. }
  152. /**
  153. * 处理请求参数
  154. */
  155. const formatListOptions = () => {
  156. return {
  157. search: search_text.value,
  158. page: page_no.value,
  159. per_page: page_count.value,
  160. agent_id: agent_id.value
  161. };
  162. };
  163. /**
  164. * 调用api请求
  165. */
  166. const handleApiRequest = () => {
  167. // const cur_agent = agent_item.value;
  168. // if (cur_agent.menuId === 1) {
  169. // // 报表合并: 报表合并需要使用领一个历史记录
  170. // }
  171. // 处理查询参数
  172. const opts = formatListOptions();
  173. // 进入加载状态
  174. api_status.value = 2;
  175. api_message.value = '';
  176. apiConversationHistoryList(opts)
  177. .then((res) => {
  178. if (res.code / 1 === 200) {
  179. const res_data = res.data;
  180. // 成功
  181. const res_list = res_data.rows; // 历史记录列表
  182. if (res_list.length < page_count.value) {
  183. // 数据已经加载完成,不能加载更多了
  184. can_loadmore.value = false;
  185. } else {
  186. can_loadmore.value = true;
  187. }
  188. dropListByType(res_list);
  189. // api请求成功
  190. api_status.value = 3;
  191. api_message.value = 'success';
  192. } else {
  193. // api请求成功,但是业务失败
  194. api_status.value = 4;
  195. api_message.value = res.msg;
  196. }
  197. })
  198. .catch((err) => {
  199. // api请求出错
  200. api_status.value = 5;
  201. api_message.value = err.message;
  202. });
  203. };
  204. /**
  205. * 请求更多数据
  206. */
  207. const handleListLoadMore = () => {
  208. if (api_status.value === 2) {
  209. return;
  210. }
  211. if (!can_loadmore.value) {
  212. // 没有更多了
  213. return;
  214. }
  215. page_no.value = page_no.value + 1; // 页码加1
  216. // 调用api
  217. handleApiRequest();
  218. };
  219. /**
  220. * 点击某个历史记录触发的事件
  221. * @param history_id
  222. */
  223. const handleListItemActive = (history_id) => {
  224. emit('historyActive', {
  225. agent_id: agent_id.value, // agent_id值
  226. target_id: history_id
  227. });
  228. };
  229. /**
  230. * 开始搜索
  231. * @param value
  232. */
  233. const handleHistorySearch = (value) => {
  234. if (api_status.value === 2) {
  235. return;
  236. }
  237. search_text.value = value; // 搜索字符串
  238. page_no.value = 1; // 页码归位
  239. list_history.value = []; // 清空数据
  240. // 调用api
  241. handleApiRequest();
  242. };
  243. /**
  244. * 选定具体的类型,此时应该执行历史记录获取的请求
  245. * 当类型选定后,获取此类型对应的历史记录数据
  246. * @param type_item
  247. */
  248. const handleTypeSelected = (type_item) => {
  249. if (api_status.value === 2) {
  250. return;
  251. }
  252. if (!type_item) {
  253. return;
  254. }
  255. // agent_item.value = type_item;
  256. // 统计数据归位
  257. count_today.value = 0;
  258. count_month.value = 0;
  259. count_earlier.value = 0;
  260. // 设置智能体的图标
  261. agent_icon.value = getIconIntelligent(type_item.icon);
  262. // 记录Agent ID
  263. agent_id.value = type_item.id;
  264. page_no.value = 1; // 页码归位
  265. list_history.value = []; // 清空数据
  266. // 调用api
  267. handleApiRequest(type_item);
  268. };
  269. onMounted(() => {
  270. // // 调用api
  271. // handleApiRequest();
  272. });
  273. </script>
  274. <style lang="css" scoped>
  275. .conversation-history-container {
  276. display: flex;
  277. justify-content: center;
  278. margin-left: auto;
  279. margin-right: auto;
  280. }
  281. .conversation-history-left {
  282. width: 25%;
  283. flex-shrink: 0;
  284. }
  285. .conversation-history-right {
  286. width: 25%;
  287. flex-shrink: 0;
  288. }
  289. .conversation-history-main {
  290. /* width: 60%; */
  291. flex-grow: 1;
  292. height: 100%;
  293. margin-left: auto;
  294. margin-right: auto;
  295. display: flex;
  296. flex-direction: column;
  297. }
  298. /* 底部占位元素 */
  299. .conversation-history-placeholder {
  300. height: 50px;
  301. }
  302. .conversation-history-main .infomations-container {
  303. display: flex;
  304. justify-content: center;
  305. align-items: center;
  306. min-height: 200px;
  307. }
  308. </style>