MessagePanne.tsx 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import ResouceModal from "@/pages/ChatAppPage/components/ResouceModal";
  2. import ThumbsMessage from "@/pages/ChatAppPage/components/ThumbsMessage";
  3. import { useEffect, useRef } from "react";
  4. import { useTranslation } from "react-i18next";
  5. import FileBs from "./FileBs";
  6. import MessageBs from "./MessageBs";
  7. import MessageSystem from "./MessageSystem";
  8. import MessageUser from "./MessageUser";
  9. import RunLog from "./RunLog";
  10. import Separator from "./Separator";
  11. import { useMessageStore } from "./messageStore";
  12. export default function MessagePanne({ useName, guideWord, loadMore, flow_type }) {
  13. const { t } = useTranslation()
  14. const { chatId, messages } = useMessageStore()
  15. // 反馈
  16. const thumbRef = useRef(null)
  17. // 溯源
  18. const sourceRef = useRef(null)
  19. // 自动滚动
  20. const messagesRef = useRef(null)
  21. const scrollLockRef = useRef(false)
  22. useEffect(() => {
  23. scrollLockRef.current = false
  24. queryLockRef.current = false
  25. }, [chatId])
  26. useEffect(() => {
  27. if (scrollLockRef.current) return
  28. messagesRef.current.scrollTop = messagesRef.current.scrollHeight;
  29. }, [messages])
  30. // 消息滚动加载
  31. const queryLockRef = useRef(false)
  32. useEffect(() => {
  33. function handleScroll() {
  34. if (queryLockRef.current) return
  35. const { scrollTop, clientHeight, scrollHeight } = messagesRef.current
  36. // 距离底部 600px内,开启自动滚动
  37. scrollLockRef.current = (scrollHeight - scrollTop - clientHeight) > 600
  38. if (messagesRef.current.scrollTop <= 90) {
  39. console.log('请求 :>> ', 1);
  40. queryLockRef.current = true
  41. loadMore()
  42. // TODO 翻页定位
  43. // 临时处理防抖
  44. setTimeout(() => {
  45. queryLockRef.current = false
  46. }, 1000);
  47. }
  48. }
  49. messagesRef.current?.addEventListener('scroll', handleScroll);
  50. return () => messagesRef.current?.removeEventListener('scroll', handleScroll)
  51. }, [messagesRef.current, messages, chatId]);
  52. return <div id="message-panne" ref={messagesRef} className="h-full overflow-y-auto scrollbar-hide pt-[50px] pb-60 px-3">
  53. {guideWord && <MessageBs
  54. key={9999}
  55. data={{ message: guideWord, isSend: false, chatKey: '', end: true, user_name: '' }} />}
  56. {
  57. messages.map(msg => {
  58. // 工厂
  59. let type = 'llm'
  60. if (msg.isSend) {
  61. type = 'user'
  62. } else if (msg.category === 'divider') {
  63. type = 'separator'
  64. } else if (msg.files?.length) {
  65. type = 'file'
  66. } else if (['tool', 'flow', 'knowledge'].includes(msg.category)) {
  67. type = 'runLog'
  68. } else if (msg.thought) {
  69. type = 'system'
  70. }
  71. switch (type) {
  72. case 'user':
  73. return <MessageUser key={msg.id} useName={useName} data={msg} />;
  74. case 'llm':
  75. return <MessageBs
  76. key={msg.id}
  77. data={msg}
  78. flow_type={flow_type}
  79. onUnlike={(chatId) => { thumbRef.current?.openModal(chatId) }}
  80. onSource={(data) => { sourceRef.current?.openModal(data) }}
  81. />;
  82. case 'system':
  83. return <MessageSystem key={msg.id} data={msg} />;
  84. case 'separator':
  85. return <Separator key={msg.id} text={msg.message || t('chat.roundOver')} />;
  86. case 'file':
  87. return <FileBs key={msg.id} data={msg} flow_type={flow_type}/>;
  88. case 'runLog':
  89. return <RunLog key={msg.id} data={msg} />;
  90. default:
  91. return <div className="text-sm mt-2 border rounded-md p-2" key={msg.id}>未知消息类型</div>;
  92. }
  93. })
  94. }
  95. {/* 踩 反馈 */}
  96. <ThumbsMessage ref={thumbRef}></ThumbsMessage>
  97. {/* 源文件类型 */}
  98. <ResouceModal ref={sourceRef}></ResouceModal>
  99. </div>
  100. };