MessageRobot.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import { View, Image, Text } from "@tarojs/components";
  2. import style from "./index.module.less";
  3. import IconCopy from "@/components/icon/IconCopy";
  4. import IconDislike from "@/components/icon/IconDislike";
  5. import IconDislikeBlue from "@/components/icon/IconDislikeBlue";
  6. import IconLike from "@/components/icon/IconLike";
  7. import IconLikeBlue from "@/components/icon/IconLikeBlue";
  8. import IconSpeaker from "@/components/icon/icon-speaker";
  9. import { TAgentDetail } from "@/types/agent";
  10. import Taro from "@tarojs/taro";
  11. import ThinkAnimation from "../think-animation/index";
  12. import { dislikeMessage, likeMessage } from "@/service/bot";
  13. import { EContentType, TMessage } from "@/types/bot";
  14. import { getLoginId, isSuccess } from "@/utils";
  15. import { useState } from "react";
  16. import { AvatarMedia } from "../AvatarMedia";
  17. interface Props {
  18. agent?: TAgentDetail | null;
  19. text: string;
  20. textReasoning: string;
  21. message: TMessage;
  22. showUser?: boolean
  23. }
  24. export default ({ agent, text, message, showUser=false, textReasoning = "" }: Props) => {
  25. const [isDislike, setIsDislike] = useState(message.isDislike);
  26. const [isLike, setIsLike] = useState(message.isLike);
  27. // console.log('helloworld: ', message)
  28. const handleCopy = (e: any, textStr: string) => {
  29. e.stopPropagation();
  30. // 手动复制并 toast 提示
  31. if (textStr) {
  32. Taro.setClipboardData({
  33. data: textStr,
  34. success() {
  35. Taro.showToast({
  36. title: "复制成功",
  37. icon: "none",
  38. });
  39. },
  40. fail(res) {
  41. console.log(res);
  42. Taro.showToast({
  43. title: "复制失败",
  44. icon: "none",
  45. });
  46. },
  47. });
  48. }
  49. };
  50. const loginId = getLoginId();
  51. const handleDislike = async () => {
  52. if (!agent?.agentId) {
  53. return;
  54. }
  55. const isDislike = !message.isDislike;
  56. const response = await dislikeMessage({
  57. agentId: agent?.agentId,
  58. dislikeReason: "",
  59. isDislike: isDislike,
  60. loginId,
  61. msgUk: message.msgUk,
  62. });
  63. if (isSuccess(response.status)) {
  64. Taro.showToast({
  65. title: isDislike ? "已差评" : "已取消差评",
  66. icon: "none",
  67. });
  68. message.isDislike = isDislike; // 更新本地状态
  69. setIsDislike(isDislike);
  70. if (isDislike && isLike) {
  71. message.isLike = false; // 取消 like
  72. setIsLike(false); // 取消 like
  73. }
  74. }
  75. };
  76. const handleLike = async () => {
  77. if (!agent?.agentId) {
  78. return;
  79. }
  80. const isLike = !message.isLike;
  81. const response = await likeMessage({
  82. agentId: agent?.agentId,
  83. isLike: isLike,
  84. loginId,
  85. msgUk: message.msgUk,
  86. });
  87. if (isSuccess(response.status)) {
  88. Taro.showToast({
  89. title: isLike ? "已喜欢" : "已取消喜欢",
  90. icon: "none",
  91. });
  92. message.isLike = isLike; // 更新本地状态
  93. // 触发 mutate 更新列表
  94. setIsLike(isLike);
  95. if (isDislike && isLike) {
  96. message.isDislike = false; // 取消 dislike
  97. setIsDislike(false); // 取消 dislike
  98. }
  99. }
  100. };
  101. // 渲染消息主体
  102. const renderMessageBody = () => {
  103. const body = message.body;
  104. // console.log(body?.contentType, body)
  105. // 渲染 QA 回答
  106. if (body?.contentType === EContentType.AiseekQA) {
  107. const payload = body?.content?.answer?.payload ?? {};
  108. const links = payload.links ?? [];
  109. const pics = payload.pics ?? [];
  110. // console.log(body)
  111. return (
  112. <View>
  113. <View className="pb-12">
  114. {pics.map((pic: string) => {
  115. return (
  116. <View>
  117. <Image src={pic} mode="widthFix" className="w-full"></Image>
  118. </View>
  119. );
  120. })}
  121. </View>
  122. <View className="pb-12">
  123. {links.map((link: string) => {
  124. return (
  125. <View onClick={(e) => handleCopy(e, link)}>
  126. <Text className="text-primary">复制链接</Text> <Text user-select>{link}</Text>
  127. </View>
  128. );
  129. })}
  130. </View>
  131. </View>
  132. );
  133. }
  134. return <></>;
  135. };
  136. return (
  137. <View>
  138. {showUser && (
  139. <View className="flex gap-8 mb-10">
  140. <View className={style.avatarContainer}>
  141. <AvatarMedia source={agent?.avatarLogo || ''} className={style.avatar} mode="aspectFill"></AvatarMedia>
  142. </View>
  143. <View className="font-medium text-16 leading-24 truncate">{agent?.name}</View>
  144. </View>
  145. )}
  146. <View className={`${style.message} ${style.messageRobot} gap-10`}>
  147. <View className={`${style.messageContent}`}>
  148. {/* {textReasoning && <View className={style.deepThinkContainer}>
  149. <View className="font-bold">深度思考:</View>
  150. <Text>
  151. {textReasoning}
  152. </Text>
  153. </View>} */}
  154. {text.length === 0 && <ThinkAnimation></ThinkAnimation>}
  155. <Text user-select>{text}</Text>
  156. {renderMessageBody()}
  157. </View>
  158. <View className="flex gap-12">
  159. <View onClick={(e) => handleCopy(e, text)}>
  160. <IconCopy />
  161. </View>
  162. {/* <IconSpeaker></IconSpeaker> */}
  163. <View onClick={() => handleDislike()}>
  164. {isDislike ? <IconDislikeBlue /> : <IconDislike />}
  165. </View>
  166. <View onClick={() => handleLike()}>
  167. {isLike ? <IconLikeBlue /> : <IconLike />}
  168. </View>
  169. </View>
  170. </View>
  171. </View>
  172. );
  173. };