Feedback.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160
  1. import { apiInterceptors, cancelFeedback, feedbackAdd, getFeedbackReasons, stopTopic } from '@/client/api';
  2. import { IChatDialogueMessageSchema } from '@/types/chat';
  3. import { CopyOutlined, DislikeOutlined, LikeOutlined } from '@ant-design/icons';
  4. import { useRequest } from 'ahooks';
  5. import { App, Button, Divider } from 'antd';
  6. import classNames from 'classnames';
  7. import copy from 'copy-to-clipboard';
  8. import React, { useContext, useState } from 'react';
  9. import { MobileChatContext } from '..';
  10. import DislikeDrawer from './DislikeDrawer';
  11. interface Tags {
  12. reason: string;
  13. reason_type: string;
  14. }
  15. const Feedback: React.FC<{
  16. content: IChatDialogueMessageSchema;
  17. index: number;
  18. chatDialogRef: React.RefObject<HTMLDivElement>;
  19. }> = ({ content, index, chatDialogRef }) => {
  20. const { conv_uid, history, scene } = useContext(MobileChatContext);
  21. const { message } = App.useApp();
  22. const [feedbackOpen, setFeedbackOpen] = useState<boolean>(false);
  23. const [status, setStatus] = useState<'like' | 'unlike' | 'none'>(content?.feedback?.feedback_type);
  24. const [list, setList] = useState<Tags[]>([]);
  25. // 复制回答
  26. const onCopyContext = async (context: any) => {
  27. const pureStr = context?.replace(/\trelations:.*/g, '');
  28. const result = copy(chatDialogRef.current?.textContent || pureStr);
  29. if (result) {
  30. if (pureStr) {
  31. message.success('复制成功');
  32. } else {
  33. message.warning('内容复制为空');
  34. }
  35. } else {
  36. message.error('复制失败');
  37. }
  38. };
  39. // 点赞 or 踩
  40. const { run: feedback, loading } = useRequest(
  41. async (params: { feedback_type: string; reason_types?: string[]; remark?: string }) =>
  42. await apiInterceptors(
  43. feedbackAdd({
  44. conv_uid: conv_uid,
  45. message_id: content.order + '',
  46. feedback_type: params.feedback_type,
  47. reason_types: params.reason_types,
  48. remark: params.remark,
  49. }),
  50. ),
  51. {
  52. manual: true,
  53. onSuccess: data => {
  54. const [, res] = data;
  55. setStatus(res?.feedback_type);
  56. message.success('反馈成功');
  57. setFeedbackOpen(false);
  58. },
  59. },
  60. );
  61. // 取消反馈
  62. const { run: cancel } = useRequest(
  63. async () => await apiInterceptors(cancelFeedback({ conv_uid: conv_uid, message_id: content?.order + '' })),
  64. {
  65. manual: true,
  66. onSuccess: data => {
  67. const [, res] = data;
  68. if (res) {
  69. setStatus('none');
  70. message.success('操作成功');
  71. }
  72. },
  73. },
  74. );
  75. // 反馈原因类型
  76. const { run: getReasonList } = useRequest(async () => await apiInterceptors(getFeedbackReasons()), {
  77. manual: true,
  78. onSuccess: data => {
  79. const [, res] = data;
  80. setList(res || []);
  81. if (res) {
  82. setFeedbackOpen(true);
  83. }
  84. },
  85. });
  86. // 终止话题
  87. const { run: stopTopicRun, loading: stopTopicLoading } = useRequest(
  88. async () => await apiInterceptors(stopTopic({ conv_id: conv_uid, round_index: 0 })),
  89. {
  90. manual: true,
  91. onSuccess: () => {
  92. message.success('操作成功');
  93. },
  94. },
  95. );
  96. return (
  97. <div className='flex items-center text-sm'>
  98. <div className='flex gap-3'>
  99. <LikeOutlined
  100. className={classNames('cursor-pointer', {
  101. 'text-[#0C75FC]': status === 'like',
  102. })}
  103. onClick={async () => {
  104. if (status === 'like') {
  105. await cancel();
  106. return;
  107. }
  108. await feedback({ feedback_type: 'like' });
  109. }}
  110. />
  111. <DislikeOutlined
  112. className={classNames('cursor-pointer', {
  113. 'text-[#0C75FC]': status === 'unlike',
  114. })}
  115. onClick={async () => {
  116. if (status === 'unlike') {
  117. await cancel();
  118. return;
  119. }
  120. await getReasonList();
  121. }}
  122. />
  123. <DislikeDrawer
  124. open={feedbackOpen}
  125. setFeedbackOpen={setFeedbackOpen}
  126. list={list}
  127. feedback={feedback}
  128. loading={loading}
  129. />
  130. </div>
  131. <Divider type='vertical' />
  132. <div className='flex items-center gap-3'>
  133. <CopyOutlined className='cursor-pointer' onClick={() => onCopyContext(content.context)} />
  134. {history.length - 1 === index && scene === 'chat_agent' && (
  135. <Button
  136. loading={stopTopicLoading}
  137. size='small'
  138. onClick={async () => {
  139. await stopTopicRun();
  140. }}
  141. className='text-xs'
  142. >
  143. 终止话题
  144. </Button>
  145. )}
  146. </div>
  147. </div>
  148. );
  149. };
  150. export default Feedback;