import React, { useRef, useEffect, useMemo, useCallback } from 'react'; import { AddIcon, ChatIcon, DeleteIcon, MoonIcon, SunIcon } from '@chakra-ui/icons'; import { Box, Button, Accordion, AccordionItem, AccordionButton, AccordionPanel, AccordionIcon, Flex, Divider, IconButton, useDisclosure, useColorMode, useColorModeValue, Menu, MenuButton, MenuList, MenuItem } from '@chakra-ui/react'; import { useUserStore } from '@/store/user'; import { useMutation, useQuery } from '@tanstack/react-query'; import { useRouter } from 'next/router'; import { getToken } from '@/utils/user'; import MyIcon from '@/components/Icon'; import WxConcat from '@/components/WxConcat'; import { getChatHistory, delChatHistoryById } from '@/api/chat'; import { getCollectionModels } from '@/api/model'; import type { ChatSiteItemType } from '../index'; import { fileDownload } from '@/utils/file'; import { htmlTemplate } from '@/constants/common'; const SlideBar = ({ chatId, modelId, history, resetChat, onClose }: { chatId: string; modelId: string; history: ChatSiteItemType[]; resetChat: (modelId?: string, chatId?: string) => void; onClose: () => void; }) => { const router = useRouter(); const { colorMode, toggleColorMode } = useColorMode(); const { myModels, getMyModels } = useUserStore(); const { isOpen: isOpenWx, onOpen: onOpenWx, onClose: onCloseWx } = useDisclosure(); const preChatId = useRef('chatId'); // 用于校验上一次chatId的情况,判断是否需要刷新历史记录 const { isSuccess } = useQuery(['getMyModels'], getMyModels, { cacheTime: 5 * 60 * 1000 }); const { data: collectionModels = [] } = useQuery([getCollectionModels], getCollectionModels); const models = useMemo(() => { const myModelList = myModels.map((item) => ({ id: item._id, name: item.name, icon: 'model' as any })); const collectionList = collectionModels .map((item) => ({ id: item._id, name: item.name, icon: 'collectionSolid' as any })) .filter((model) => !myModelList.find((item) => item.id === model.id)); return myModelList.concat(collectionList); }, [collectionModels, myModels]); const { data: chatHistory = [], mutate: loadChatHistory } = useMutation({ mutationFn: getChatHistory }); // update history useEffect(() => { if (chatId && preChatId.current === '') { loadChatHistory(); } preChatId.current = chatId; }, [chatId, loadChatHistory]); // init history useEffect(() => { setTimeout(() => { loadChatHistory(); }, 1000); }, [loadChatHistory]); /** * export md */ const onclickExportMd = useCallback(() => { fileDownload({ text: history.map((item) => item.value).join('\n'), type: 'text/markdown', filename: 'chat.md' }); }, [history]); const getHistoryHtml = useCallback(() => { const historyDom = document.getElementById('history'); if (!historyDom) return; const dom = Array.from(historyDom.children).map((child, i) => { const avatar = ``; const chatContent = child.querySelector('.markdown'); if (!chatContent) { return ''; } const chatContentClone = chatContent.cloneNode(true) as HTMLDivElement; const codeHeader = chatContentClone.querySelectorAll('.code-header'); codeHeader.forEach((childElement: any) => { childElement.remove(); }); return `
${avatar} ${chatContentClone.outerHTML}
`; }); const html = htmlTemplate.replace('{{CHAT_CONTENT}}', dom.join('\n')); return html; }, []); const onclickExportHtml = useCallback(() => { const html = getHistoryHtml(); html && fileDownload({ text: html, type: 'text/html', filename: '聊天记录.html' }); }, [getHistoryHtml]); const onclickExportPdf = useCallback(() => { const html = getHistoryHtml(); html && // @ts-ignore html2pdf(html, { margin: 0, filename: `聊天记录.pdf` }); }, [getHistoryHtml]); const RenderHistory = () => ( <> {chatHistory.map((item) => ( { if (item._id === chatId) return; preChatId.current = 'chatId'; resetChat(item.modelId, item._id); onClose(); }} > {item.title} } variant={'unstyled'} aria-label={'edit'} size={'xs'} onClick={async (e) => { e.stopPropagation(); await delChatHistoryById(item._id); loadChatHistory(); if (item._id === chatId) { resetChat(); } }} /> ))} ); const RenderButton = ({ onClick, children }: { onClick: () => void; children: JSX.Element | string; }) => ( {children} ); return ( {/* 新对话 */} {getToken() && ( )} {/* 我的模型 & 历史记录 折叠框*/} {isSuccess && ( <> {models.map((item) => ( { if (item.id === modelId) return; resetChat(item.id); onClose(); }} > {item.name} ))} )} 历史记录 {history.length > 0 && ( 导出聊天 HTML格式 PDF格式 Markdown格式 )} router.push('/')}> <> 首页 router.push('/number/setting')}> <> 充值 交流群 : } aria-label={''} variant={'outline'} w={'16px'} colorScheme={'white'} _hover={{ backgroundColor: 'rgba(255,255,255,0.2)' }} onClick={toggleColorMode} /> {/* wx 联系 */} {isOpenWx && } ); }; export default SlideBar;