diff --git a/client/src/components/Avatar/index.tsx b/client/src/components/Avatar/index.tsx index acf426d03..ab45d8c0c 100644 --- a/client/src/components/Avatar/index.tsx +++ b/client/src/components/Avatar/index.tsx @@ -7,6 +7,7 @@ const Avatar = ({ w = '30px', ...props }: ImageProps) => { return ( { + const onExportChat = useCallback( + ({ type, history }: { type: ExportChatType; history: ChatItemType[] }) => { + const getHistoryHtml = () => { + 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 map: Record void> = { + md: () => { + fileDownload({ + text: history.map((item) => item.value).join('\n\n'), + type: 'text/markdown', + filename: 'chat.md' + }); + }, + html: () => { + const html = getHistoryHtml(); + html && + fileDownload({ + text: html, + type: 'text/html', + filename: '聊天记录.html' + }); + }, + pdf: () => { + const html = getHistoryHtml(); + + html && + // @ts-ignore + html2pdf(html, { + margin: 0, + filename: `聊天记录.pdf` + }); + } + }; + + map[type](); + }, + [] + ); + + return { + onExportChat + }; +}; diff --git a/client/src/pages/api/openapi/v1/chat/completions.ts b/client/src/pages/api/openapi/v1/chat/completions.ts index 4c35dae05..f67464f42 100644 --- a/client/src/pages/api/openapi/v1/chat/completions.ts +++ b/client/src/pages/api/openapi/v1/chat/completions.ts @@ -124,6 +124,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex historyId, newHistoryId, appId, + variables, prompts: [ prompt, { diff --git a/client/src/pages/chat/components/ToolMenu.tsx b/client/src/pages/chat/components/ToolMenu.tsx new file mode 100644 index 000000000..960f42a87 --- /dev/null +++ b/client/src/pages/chat/components/ToolMenu.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { useChatBox } from '@/components/ChatBox'; +import { ChatItemType } from '@/types/chat'; +import { Menu, MenuButton, MenuList, MenuItem } from '@chakra-ui/react'; +import MyIcon from '@/components/Icon'; + +const ToolMenu = ({ history }: { history: ChatItemType[] }) => { + const { onExportChat } = useChatBox(); + return ( + + { + e.stopPropagation(); + }} + > + + + + onExportChat({ type: 'html', history })}>导出HTML格式 + onExportChat({ type: 'pdf', history })}>导出PDF格式 + onExportChat({ type: 'md', history })}>导出Markdown格式 + + + ); +}; + +export default ToolMenu; diff --git a/client/src/pages/chat/index.tsx b/client/src/pages/chat/index.tsx index a8eddf48c..7253d60a1 100644 --- a/client/src/pages/chat/index.tsx +++ b/client/src/pages/chat/index.tsx @@ -32,12 +32,13 @@ import { useChatStore } from '@/store/chat'; import { useLoading } from '@/hooks/useLoading'; import ChatBox, { type ComponentRef, type StartChatFnProps } from '@/components/ChatBox'; +import { ChatHistoryItemType } from '@/types/chat'; import PageContainer from '@/components/PageContainer'; import SideBar from '@/components/SideBar'; import ChatHistorySlider from './components/ChatHistorySlider'; import SliderApps from './components/SliderApps'; import Tag from '@/components/Tag'; -import { ChatHistoryItemType } from '@/types/chat'; +import ToolMenu from './components/ToolMenu'; const Chat = () => { const router = useRouter(); @@ -316,6 +317,8 @@ const Chat = () => { /> )} + + {/* chat box */}