mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 21:13:50 +00:00
fix: save chat
This commit is contained in:
@@ -7,6 +7,7 @@ const Avatar = ({ w = '30px', ...props }: ImageProps) => {
|
||||
return (
|
||||
<Image
|
||||
fallbackSrc={LOGO_ICON}
|
||||
fallbackStrategy={'onError'}
|
||||
borderRadius={'50%'}
|
||||
objectFit={'cover'}
|
||||
alt=""
|
||||
|
@@ -8,7 +8,7 @@ import React, {
|
||||
ForwardedRef
|
||||
} from 'react';
|
||||
import { throttle } from 'lodash';
|
||||
import { ChatSiteItemType } from '@/types/chat';
|
||||
import { ChatItemType, ChatSiteItemType, ExportChatType } from '@/types/chat';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { useCopyData, voiceBroadcast, hasVoiceApi } from '@/utils/tools';
|
||||
import { Box, Card, Flex, Input, Textarea, Button, useTheme } from '@chakra-ui/react';
|
||||
@@ -28,6 +28,8 @@ import MySelect from '@/components/Select';
|
||||
import { MessageItemType } from '@/pages/api/openapi/v1/chat/completions';
|
||||
import styles from './index.module.scss';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
import { fileDownload } from '@/utils/file';
|
||||
import { htmlTemplate } from '@/constants/common';
|
||||
|
||||
const textareaMinH = '22px';
|
||||
export type StartChatFnProps = {
|
||||
@@ -607,3 +609,76 @@ const ChatBox = (
|
||||
};
|
||||
|
||||
export default React.memo(forwardRef(ChatBox));
|
||||
|
||||
export const useChatBox = () => {
|
||||
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 = `<img src="${
|
||||
child.querySelector<HTMLImageElement>('.avatar')?.src
|
||||
}" alt="" />`;
|
||||
|
||||
const chatContent = child.querySelector<HTMLDivElement>('.markdown');
|
||||
|
||||
if (!chatContent) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const chatContentClone = chatContent.cloneNode(true) as HTMLDivElement;
|
||||
|
||||
const codeHeader = chatContentClone.querySelectorAll('.code-header');
|
||||
codeHeader.forEach((childElement: any) => {
|
||||
childElement.remove();
|
||||
});
|
||||
|
||||
return `<div class="chat-item">
|
||||
${avatar}
|
||||
${chatContentClone.outerHTML}
|
||||
</div>`;
|
||||
});
|
||||
|
||||
const html = htmlTemplate.replace('{{CHAT_CONTENT}}', dom.join('\n'));
|
||||
return html;
|
||||
};
|
||||
|
||||
const map: Record<ExportChatType, () => 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
|
||||
};
|
||||
};
|
||||
|
@@ -124,6 +124,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
historyId,
|
||||
newHistoryId,
|
||||
appId,
|
||||
variables,
|
||||
prompts: [
|
||||
prompt,
|
||||
{
|
||||
|
30
client/src/pages/chat/components/ToolMenu.tsx
Normal file
30
client/src/pages/chat/components/ToolMenu.tsx
Normal file
@@ -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 (
|
||||
<Menu autoSelect={false} isLazy>
|
||||
<MenuButton
|
||||
_hover={{ bg: 'myWhite.600 ' }}
|
||||
cursor={'pointer'}
|
||||
borderRadius={'md'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<MyIcon name={'more'} w={'14px'} p={2} />
|
||||
</MenuButton>
|
||||
<MenuList color={'myGray.700'} minW={`90px !important`}>
|
||||
<MenuItem onClick={() => onExportChat({ type: 'html', history })}>导出HTML格式</MenuItem>
|
||||
<MenuItem onClick={() => onExportChat({ type: 'pdf', history })}>导出PDF格式</MenuItem>
|
||||
<MenuItem onClick={() => onExportChat({ type: 'md', history })}>导出Markdown格式</MenuItem>
|
||||
</MenuList>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export default ToolMenu;
|
@@ -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 = () => {
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Box flex={1} />
|
||||
<ToolMenu history={chatData.history} />
|
||||
</Flex>
|
||||
{/* chat box */}
|
||||
<Box flex={1}>
|
||||
|
Reference in New Issue
Block a user