index.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import { Trash2 } from "lucide-react";
  2. import { useContext, useEffect, useRef, useState } from "react";
  3. import { useTranslation } from "react-i18next";
  4. import { bsconfirm } from "../../alerts/confirm";
  5. import { TabsContext } from "../../contexts/tabsContext";
  6. import { deleteChatApi, getChatsApi } from "../../controllers/API";
  7. import { getFlowApi, readOnlineFlows } from "../../controllers/API/flow";
  8. import { FlowType } from "../../types/flow";
  9. import { generateUUID } from "../../utils";
  10. import SkillTemps from "../SkillPage/components/SkillTemps";
  11. import ChatPanne from "./components/ChatPanne";
  12. import { captureAndAlertRequestErrorHoc } from "../../controllers/request";
  13. import { useDebounce, useTable } from "../../util/hook";
  14. import duihuaDel from "../../assets/chat/duihua-del.png";
  15. import robot from "../../assets/robot.png";
  16. import robot2 from "../../assets/robot2.png";
  17. import robot3 from "../../assets/robot3.png";
  18. import duihuaItemTop from "../../assets/chat/duihua-item-top.png";
  19. import duihuaItemJia from "../../assets/chat/duihua-item-+.png";
  20. import duihuaItemGuan from "../../assets/chat/duihua-item-x.png";
  21. export default function SkillChatPage() {
  22. const [open, setOpen] = useState(false)
  23. const [face, setFace] = useState(true);
  24. const { t } = useTranslation()
  25. const { flow: initFlow } = useContext(TabsContext);
  26. const [flow, setFlow] = useState<FlowType>(null)
  27. const {
  28. data: onlineFlows,
  29. loading,
  30. search,
  31. } = useTable<FlowType>({}, (param) =>
  32. readOnlineFlows(param.page, param.keyword).then((res) => {
  33. return res;
  34. })
  35. );
  36. // 对话列表
  37. const { chatList, chatId, chatsRef, setChatId, addChat, deleteChat } = useChatList()
  38. const chatIdRef = useRef('')
  39. // select flow
  40. const handlerSelectFlow = async (node: FlowType) => {
  41. // 会话ID
  42. chatIdRef.current = generateUUID(32)
  43. setOpen(false)
  44. // add list
  45. addChat({
  46. "flow_name": node.name,
  47. "flow_description": node.description,
  48. "flow_id": node.id,
  49. "chat_id": chatIdRef.current,
  50. "create_time": "-",
  51. "update_time": "-"
  52. })
  53. const flow = await getFlowApi(node.id)
  54. setFlow(flow)
  55. setChatId(chatIdRef.current)
  56. setFace(false)
  57. }
  58. // select chat
  59. const handleSelectChat = useDebounce(async (chat) => {
  60. if (chat.chat_id === chatId) return
  61. chatIdRef.current = chat.chat_id
  62. const flow = initFlow?.id === chat.flow_id ? initFlow : await getFlowApi(chat.flow_id)
  63. // if (!flow) {
  64. // setInputState({ lock: true, errorCode: '1004' })
  65. // clearHistory()
  66. // return setFace(false)
  67. // }
  68. setFlow(flow)
  69. setChatId(chat.chat_id)
  70. setFace(false)
  71. }, 100, false)
  72. // del
  73. const handleDeleteChat = (e, id) => {
  74. e.stopPropagation();
  75. bsconfirm({
  76. desc: t('chat.confirmDeleteChat'),
  77. onOk(next) {
  78. deleteChat(id);
  79. setFace(true)
  80. next()
  81. }
  82. })
  83. }
  84. return <div className="flex">
  85. <div className="h-screen w-[288px] border-r xinDuiHua-box relative">
  86. <div className="xinDuiHua absolute">
  87. <div className="xinDuiHua-btn cursor-pointer" onClick={() => setOpen(true)}>{t('chat.newChat')}</div>
  88. {/* <div className="xinDuiHua-del cursor-pointer">
  89. <img src={duihuaDel} alt=""/>
  90. </div> */}
  91. </div>
  92. <div ref={chatsRef} className="absolute w-[100%] scroll p-[10px] h-full overflow-y-scroll no-scrollbar pt-[63px]">
  93. {
  94. chatList.map((chat, i) => (
  95. <div key={chat.chat_id}
  96. className={` group item xinDuiHua-list-item relative hover:xinDuiHua-list-active cursor-pointer dark:hover:xinDuiHua-list-active ${chatId === chat.chat_id && 'xinDuiHua-list-active dark:xinDuiHua-list-active'}`}
  97. onClick={() => handleSelectChat(chat)}>
  98. <div>
  99. {(chat.flow_id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || chat.flow_id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} alt=""/>}
  100. {chat.flow_id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} alt=""/>}
  101. {(chat.flow_id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && chat.flow_id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && chat.flow_id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} alt=""/>}
  102. {/* <img src={robot} alt=""/> */}
  103. <div>
  104. <p>{chat.flow_name}</p>
  105. {/* <div>离线</div> */}
  106. </div>
  107. </div>
  108. <div>
  109. {/* <img src={duihuaItemTop} alt=""/>
  110. <img src={duihuaItemJia} alt=""/> */}
  111. </div>
  112. <img className="absolute w-[10px] top-[5px] right-[5px] hidden group-hover:block" src={duihuaItemGuan} onClick={(e) => handleDeleteChat(e, chat.chat_id)} alt=""/>
  113. {/* <span className="text-xs text-gray-500">{chat.flow_description}</span> */}
  114. {/* <Trash2 size={14} className="absolute bottom-2 right-2 text-gray-400 hidden group-hover:block" onClick={(e) => handleDeleteChat(e, chat.chat_id)}></Trash2> */}
  115. </div>
  116. ))
  117. }
  118. </div>
  119. </div>
  120. {/* chat */}
  121. {face
  122. ? <div className="flex-1 chat-box h-screen overflow-hidden relative">
  123. <p className="text-center mt-[100px] text-sm text-gray-600">{t('chat.selectChat')}</p>
  124. </div>
  125. : <div className="flex-1 chat-box h-screen relative">
  126. {flow && <ChatPanne chatId={chatId} flow={flow} />}
  127. </div>}
  128. {/* 选择对话技能 */}
  129. <SkillTemps
  130. flows={onlineFlows}
  131. title={t('chat.skillTempsTitle')}
  132. desc={t('chat.skillTempsDesc')}
  133. open={open} setOpen={setOpen}
  134. onSelect={handlerSelectFlow}></SkillTemps>
  135. </div>
  136. };
  137. /**
  138. * 本地对话列表
  139. */
  140. const useChatList = () => {
  141. const [id, setId] = useState('')
  142. const [chatList, setChatList] = useState([])
  143. const chatsRef = useRef(null)
  144. useEffect(() => {
  145. getChatsApi().then(setChatList)
  146. }, [])
  147. return {
  148. chatList,
  149. chatId: id,
  150. chatsRef,
  151. setChatId: setId,
  152. addChat: (chat) => {
  153. const newList = [chat, ...chatList]
  154. // localStorage.setItem(ITEM_KEY, JSON.stringify(newList))
  155. setChatList(newList)
  156. setId(chat.chat_id)
  157. setTimeout(() => {
  158. chatsRef.current.scrollTop = 1
  159. }, 0);
  160. },
  161. deleteChat: (id: string) => {
  162. // api
  163. captureAndAlertRequestErrorHoc(deleteChatApi(id).then(res => {
  164. setChatList(oldList => oldList.filter(item => item.chat_id !== id))
  165. }))
  166. }
  167. }
  168. }