mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-22 20:37:48 +00:00
fix: chat page render performance (#2784)
* fix: chat page render performance * fix: ts
This commit is contained in:
@@ -103,3 +103,4 @@ weight: 813
|
||||
17. 修复 - 知识库选择权限问题。
|
||||
18. 修复 - 空 chatId 发起对话,首轮携带用户选择时会异常。
|
||||
19. 修复 - createDataset 接口,intro 为赋值。
|
||||
20. 修复 - 对话框渲染性能问题。
|
||||
|
@@ -11,12 +11,12 @@ import { eventBus, EventNameEnum } from '@/web/common/utils/eventbus';
|
||||
import { getChatRecords } from '@/web/core/chat/api';
|
||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { getNanoid } from '@fastgpt/global/common/string/tools';
|
||||
import { GetChatRecordsProps } from '@/global/core/chat/api';
|
||||
import { useScrollPagination } from '@fastgpt/web/hooks/useScrollPagination';
|
||||
import { PaginationResponse } from '../../../../../../../packages/web/common/fetch/type';
|
||||
import { PaginationResponse } from '@fastgpt/web/common/fetch/type';
|
||||
import type { getPaginationRecordsBody } from '@/pages/api/core/chat/getPaginationRecords';
|
||||
import { GetChatTypeEnum } from '@/global/core/chat/constants';
|
||||
|
||||
export const useChat = () => {
|
||||
export const useChat = (params?: { chatId?: string; appId: string; type?: GetChatTypeEnum }) => {
|
||||
const ChatBoxRef = useRef<ChatComponentRef>(null);
|
||||
const variablesForm = useForm<ChatBoxInputFormType>();
|
||||
// plugin
|
||||
@@ -49,38 +49,41 @@ export const useChat = () => {
|
||||
ChatBoxRef.current?.restartChat?.();
|
||||
}, [variablesForm]);
|
||||
|
||||
const useChatScrollData = useCallback((params: GetChatRecordsProps) => {
|
||||
return useScrollPagination(
|
||||
async (data: getPaginationRecordsBody): Promise<PaginationResponse<ChatSiteItemType>> => {
|
||||
const res = await getChatRecords(data);
|
||||
const {
|
||||
data: chatRecords,
|
||||
ScrollData,
|
||||
setData: setChatRecords,
|
||||
total: totalRecordsCount
|
||||
} = useScrollPagination(
|
||||
async (data: getPaginationRecordsBody): Promise<PaginationResponse<ChatSiteItemType>> => {
|
||||
const res = await getChatRecords(data);
|
||||
|
||||
// First load scroll to bottom
|
||||
if (data.offset === 0) {
|
||||
function scrollToBottom() {
|
||||
requestAnimationFrame(
|
||||
ChatBoxRef?.current ? () => ChatBoxRef?.current?.scrollToBottom?.() : scrollToBottom
|
||||
);
|
||||
}
|
||||
scrollToBottom();
|
||||
// First load scroll to bottom
|
||||
if (data.offset === 0) {
|
||||
function scrollToBottom() {
|
||||
requestAnimationFrame(
|
||||
ChatBoxRef?.current ? () => ChatBoxRef?.current?.scrollToBottom?.() : scrollToBottom
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...res,
|
||||
list: res.list.map((item) => ({
|
||||
...item,
|
||||
dataId: item.dataId || getNanoid(),
|
||||
status: ChatStatusEnum.finish
|
||||
}))
|
||||
};
|
||||
},
|
||||
{
|
||||
pageSize: 10,
|
||||
refreshDeps: [params],
|
||||
params,
|
||||
scrollLoadType: 'top'
|
||||
scrollToBottom();
|
||||
}
|
||||
);
|
||||
}, []);
|
||||
|
||||
return {
|
||||
...res,
|
||||
list: res.list.map((item) => ({
|
||||
...item,
|
||||
dataId: item.dataId || getNanoid(),
|
||||
status: ChatStatusEnum.finish
|
||||
}))
|
||||
};
|
||||
},
|
||||
{
|
||||
pageSize: 10,
|
||||
refreshDeps: [params],
|
||||
params,
|
||||
scrollLoadType: 'top'
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
ChatBoxRef,
|
||||
@@ -89,7 +92,10 @@ export const useChat = () => {
|
||||
setPluginRunTab,
|
||||
clearChatRecords,
|
||||
resetVariables,
|
||||
useChatScrollData
|
||||
chatRecords,
|
||||
ScrollData,
|
||||
setChatRecords,
|
||||
totalRecordsCount
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -47,14 +47,11 @@ const DetailLogsModal = ({
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
useChatScrollData
|
||||
} = useChat();
|
||||
const {
|
||||
data: chatRecords,
|
||||
chatRecords,
|
||||
ScrollData,
|
||||
setData: setChatRecords,
|
||||
total: totalRecordsCount
|
||||
} = useChatScrollData(params);
|
||||
setChatRecords,
|
||||
totalRecordsCount
|
||||
} = useChat(params);
|
||||
|
||||
const { data: chat, isFetching } = useQuery(
|
||||
['getChatDetail', chatId],
|
||||
|
@@ -164,4 +164,4 @@ const SliderApps = ({ apps, activeAppId }: { apps: AppListItemType[]; activeAppI
|
||||
);
|
||||
};
|
||||
|
||||
export default SliderApps;
|
||||
export default React.memo(SliderApps);
|
||||
|
@@ -77,14 +77,11 @@ const Chat = ({
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
useChatScrollData
|
||||
} = useChat();
|
||||
const {
|
||||
data: chatRecords,
|
||||
chatRecords,
|
||||
ScrollData,
|
||||
setData: setChatRecords,
|
||||
total: totalRecordsCount
|
||||
} = useChatScrollData(params);
|
||||
setChatRecords,
|
||||
totalRecordsCount
|
||||
} = useChat(params);
|
||||
|
||||
// get chat app info
|
||||
const [chatData, setChatData] = useState<InitChatResponse>(defaultChatData);
|
||||
@@ -166,6 +163,57 @@ const Chat = ({
|
||||
);
|
||||
const loading = isLoading;
|
||||
|
||||
const RenderHistorySlider = useMemo(() => {
|
||||
const Children = (
|
||||
<ChatHistorySlider
|
||||
confirmClearText={t('common:core.chat.Confirm to clear history')}
|
||||
appId={appId}
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
onDelHistory={(e) => onDelHistory({ ...e, appId })}
|
||||
onClearHistory={() => {
|
||||
onClearHistories({ appId });
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
onUpdateHistory({ ...e, appId });
|
||||
}}
|
||||
onSetCustomTitle={async (e) => {
|
||||
onUpdateHistory({
|
||||
appId,
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
return isPc || !appId ? (
|
||||
<SideBar>{Children}</SideBar>
|
||||
) : (
|
||||
<Drawer
|
||||
isOpen={isOpenSlider}
|
||||
placement="left"
|
||||
autoFocus={false}
|
||||
size={'xs'}
|
||||
onClose={onCloseSlider}
|
||||
>
|
||||
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
||||
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}, [
|
||||
appId,
|
||||
chatData.app.avatar,
|
||||
chatData.app.name,
|
||||
isOpenSlider,
|
||||
isPc,
|
||||
onClearHistories,
|
||||
onCloseSlider,
|
||||
onDelHistory,
|
||||
onUpdateHistory,
|
||||
t
|
||||
]);
|
||||
|
||||
return (
|
||||
<Flex h={'100%'}>
|
||||
<NextHead title={chatData.app.name} icon={chatData.app.avatar}></NextHead>
|
||||
@@ -179,43 +227,7 @@ const Chat = ({
|
||||
<PageContainer isLoading={loading} flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
||||
<Flex h={'100%'} flexDirection={['column', 'row']}>
|
||||
{/* pc always show history. */}
|
||||
{((children: React.ReactNode) => {
|
||||
return isPc || !appId ? (
|
||||
<SideBar>{children}</SideBar>
|
||||
) : (
|
||||
<Drawer
|
||||
isOpen={isOpenSlider}
|
||||
placement="left"
|
||||
autoFocus={false}
|
||||
size={'xs'}
|
||||
onClose={onCloseSlider}
|
||||
>
|
||||
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
||||
<DrawerContent maxWidth={'75vw'}>{children}</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
})(
|
||||
<ChatHistorySlider
|
||||
confirmClearText={t('common:core.chat.Confirm to clear history')}
|
||||
appId={appId}
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
onDelHistory={(e) => onDelHistory({ ...e, appId })}
|
||||
onClearHistory={() => {
|
||||
onClearHistories({ appId });
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
onUpdateHistory({ ...e, appId });
|
||||
}}
|
||||
onSetCustomTitle={async (e) => {
|
||||
onUpdateHistory({
|
||||
appId,
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{RenderHistorySlider}
|
||||
{/* chat container */}
|
||||
<Flex
|
||||
position={'relative'}
|
||||
|
@@ -4,11 +4,7 @@ import { Box, Flex, Drawer, DrawerOverlay, DrawerContent } from '@chakra-ui/reac
|
||||
import { streamFetch } from '@/web/common/api/fetch';
|
||||
import SideBar from '@/components/SideBar';
|
||||
import { GPTMessages2Chats } from '@fastgpt/global/core/chat/adapt';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet(
|
||||
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWSYZ1234567890_',
|
||||
24
|
||||
);
|
||||
|
||||
import ChatBox from '@/components/core/chat/ChatContainer/ChatBox';
|
||||
import type { StartChatFnProps } from '@/components/core/chat/ChatContainer/type';
|
||||
|
||||
@@ -19,7 +15,6 @@ import { serviceSideProps } from '@/web/common/utils/i18n';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { delChatRecordById, getInitOutLinkChatInfo } from '@/web/core/chat/api';
|
||||
import { getChatTitleFromChatMessage } from '@fastgpt/global/core/chat/utils';
|
||||
import { ChatStatusEnum } from '@fastgpt/global/core/chat/constants';
|
||||
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
|
||||
import { OutLinkWithAppType } from '@fastgpt/global/support/outLink/type';
|
||||
import { addLog } from '@fastgpt/service/common/system/log';
|
||||
@@ -87,14 +82,6 @@ const OutLink = ({
|
||||
onChangeChatId
|
||||
} = useContextSelector(ChatContext, (v) => v);
|
||||
|
||||
const {
|
||||
ChatBoxRef,
|
||||
variablesForm,
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
useChatScrollData
|
||||
} = useChat();
|
||||
const params = useMemo(() => {
|
||||
return {
|
||||
chatId,
|
||||
@@ -105,11 +92,16 @@ const OutLink = ({
|
||||
};
|
||||
}, [chatData.appId, chatId, outLinkUid, shareId]);
|
||||
const {
|
||||
data: chatRecords,
|
||||
ChatBoxRef,
|
||||
variablesForm,
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
chatRecords,
|
||||
ScrollData,
|
||||
setData: setChatRecords,
|
||||
total: totalRecordsCount
|
||||
} = useChatScrollData(params);
|
||||
setChatRecords,
|
||||
totalRecordsCount
|
||||
} = useChat(params);
|
||||
|
||||
const startChat = useCallback(
|
||||
async ({
|
||||
@@ -233,6 +225,73 @@ const OutLink = ({
|
||||
useMount(() => {
|
||||
setIdEmbed(window !== top);
|
||||
});
|
||||
|
||||
const RenderHistoryList = useMemo(() => {
|
||||
const Children = (
|
||||
<ChatHistorySlider
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
confirmClearText={t('common:core.chat.Confirm to clear share chat history')}
|
||||
onDelHistory={({ chatId }) =>
|
||||
onDelHistory({ appId: chatData.appId, chatId, shareId, outLinkUid })
|
||||
}
|
||||
onClearHistory={() => {
|
||||
onClearHistories({ shareId, outLinkUid });
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
onUpdateHistory({
|
||||
...e,
|
||||
appId: chatData.appId,
|
||||
shareId,
|
||||
outLinkUid
|
||||
});
|
||||
}}
|
||||
onSetCustomTitle={(e) => {
|
||||
onUpdateHistory({
|
||||
appId: chatData.appId,
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title,
|
||||
shareId,
|
||||
outLinkUid
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
if (showHistory !== '1') return null;
|
||||
|
||||
return isPc ? (
|
||||
<SideBar>{Children}</SideBar>
|
||||
) : (
|
||||
<Drawer
|
||||
isOpen={isOpenSlider}
|
||||
placement="left"
|
||||
autoFocus={false}
|
||||
size={'xs'}
|
||||
onClose={onCloseSlider}
|
||||
>
|
||||
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
||||
<DrawerContent maxWidth={'75vw'} boxShadow={'2px 0 10px rgba(0,0,0,0.15)'}>
|
||||
{Children}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}, [
|
||||
chatData.app.avatar,
|
||||
chatData.app.name,
|
||||
chatData.appId,
|
||||
isOpenSlider,
|
||||
isPc,
|
||||
onClearHistories,
|
||||
onCloseSlider,
|
||||
onDelHistory,
|
||||
onUpdateHistory,
|
||||
outLinkUid,
|
||||
shareId,
|
||||
showHistory,
|
||||
t
|
||||
]);
|
||||
|
||||
const loading = isLoading;
|
||||
|
||||
return (
|
||||
@@ -244,54 +303,7 @@ const OutLink = ({
|
||||
: { p: [0, 5] })}
|
||||
>
|
||||
<Flex h={'100%'} flexDirection={['column', 'row']}>
|
||||
{showHistory === '1' &&
|
||||
((children: React.ReactNode) => {
|
||||
return isPc ? (
|
||||
<SideBar>{children}</SideBar>
|
||||
) : (
|
||||
<Drawer
|
||||
isOpen={isOpenSlider}
|
||||
placement="left"
|
||||
autoFocus={false}
|
||||
size={'xs'}
|
||||
onClose={onCloseSlider}
|
||||
>
|
||||
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
||||
<DrawerContent maxWidth={'75vw'} boxShadow={'2px 0 10px rgba(0,0,0,0.15)'}>
|
||||
{children}
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
})(
|
||||
<ChatHistorySlider
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
confirmClearText={t('common:core.chat.Confirm to clear share chat history')}
|
||||
onDelHistory={({ chatId }) =>
|
||||
onDelHistory({ appId: chatData.appId, chatId, shareId, outLinkUid })
|
||||
}
|
||||
onClearHistory={() => {
|
||||
onClearHistories({ shareId, outLinkUid });
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
onUpdateHistory({
|
||||
...e,
|
||||
appId: chatData.appId,
|
||||
shareId,
|
||||
outLinkUid
|
||||
});
|
||||
}}
|
||||
onSetCustomTitle={(e) => {
|
||||
onUpdateHistory({
|
||||
appId: chatData.appId,
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title,
|
||||
shareId,
|
||||
outLinkUid
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{RenderHistoryList}
|
||||
|
||||
{/* chat container */}
|
||||
<Flex
|
||||
|
@@ -67,14 +67,6 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
onChangeChatId
|
||||
} = useContextSelector(ChatContext, (v) => v);
|
||||
|
||||
const {
|
||||
ChatBoxRef,
|
||||
variablesForm,
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
useChatScrollData
|
||||
} = useChat();
|
||||
const params = useMemo(() => {
|
||||
return {
|
||||
appId,
|
||||
@@ -85,11 +77,16 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
};
|
||||
}, [appId, chatId, teamId, teamToken]);
|
||||
const {
|
||||
data: chatRecords,
|
||||
ChatBoxRef,
|
||||
variablesForm,
|
||||
pluginRunTab,
|
||||
setPluginRunTab,
|
||||
resetVariables,
|
||||
chatRecords,
|
||||
ScrollData,
|
||||
setData: setChatRecords,
|
||||
total: totalRecordsCount
|
||||
} = useChatScrollData(params);
|
||||
setChatRecords,
|
||||
totalRecordsCount
|
||||
} = useChat(params);
|
||||
|
||||
const startChat = useCallback(
|
||||
async ({
|
||||
@@ -181,6 +178,61 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
}
|
||||
);
|
||||
|
||||
const RenderHistoryList = useMemo(() => {
|
||||
const Children = (
|
||||
<ChatHistorySlider
|
||||
appId={appId}
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
confirmClearText={t('common:core.chat.Confirm to clear history')}
|
||||
onDelHistory={(e) => onDelHistory({ ...e, appId, teamId, teamToken })}
|
||||
onClearHistory={() => {
|
||||
onClearHistories({ appId, teamId, teamToken });
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
onUpdateHistory({ ...e, teamId, teamToken, appId });
|
||||
}}
|
||||
onSetCustomTitle={async (e) => {
|
||||
onUpdateHistory({
|
||||
appId,
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title,
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
|
||||
return isPc || !appId ? (
|
||||
<SideBar>{Children}</SideBar>
|
||||
) : (
|
||||
<Drawer
|
||||
isOpen={isOpenSlider}
|
||||
placement="left"
|
||||
autoFocus={false}
|
||||
size={'xs'}
|
||||
onClose={onCloseSlider}
|
||||
>
|
||||
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
||||
<DrawerContent maxWidth={'75vw'}>{Children}</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
}, [
|
||||
appId,
|
||||
chatData.app.avatar,
|
||||
chatData.app.name,
|
||||
isOpenSlider,
|
||||
isPc,
|
||||
onClearHistories,
|
||||
onCloseSlider,
|
||||
onDelHistory,
|
||||
onUpdateHistory,
|
||||
t,
|
||||
teamId,
|
||||
teamToken
|
||||
]);
|
||||
|
||||
const loading = isLoading;
|
||||
|
||||
return (
|
||||
@@ -195,45 +247,7 @@ const Chat = ({ myApps }: { myApps: AppListItemType[] }) => {
|
||||
|
||||
<PageContainer isLoading={loading} flex={'1 0 0'} w={0} p={[0, '16px']} position={'relative'}>
|
||||
<Flex h={'100%'} flexDirection={['column', 'row']} bg={'white'}>
|
||||
{((children: React.ReactNode) => {
|
||||
return isPc || !appId ? (
|
||||
<SideBar>{children}</SideBar>
|
||||
) : (
|
||||
<Drawer
|
||||
isOpen={isOpenSlider}
|
||||
placement="left"
|
||||
autoFocus={false}
|
||||
size={'xs'}
|
||||
onClose={onCloseSlider}
|
||||
>
|
||||
<DrawerOverlay backgroundColor={'rgba(255,255,255,0.5)'} />
|
||||
<DrawerContent maxWidth={'75vw'}>{children}</DrawerContent>
|
||||
</Drawer>
|
||||
);
|
||||
})(
|
||||
<ChatHistorySlider
|
||||
appId={appId}
|
||||
appName={chatData.app.name}
|
||||
appAvatar={chatData.app.avatar}
|
||||
confirmClearText={t('common:core.chat.Confirm to clear history')}
|
||||
onDelHistory={(e) => onDelHistory({ ...e, appId, teamId, teamToken })}
|
||||
onClearHistory={() => {
|
||||
onClearHistories({ appId, teamId, teamToken });
|
||||
}}
|
||||
onSetHistoryTop={(e) => {
|
||||
onUpdateHistory({ ...e, teamId, teamToken, appId });
|
||||
}}
|
||||
onSetCustomTitle={async (e) => {
|
||||
onUpdateHistory({
|
||||
appId,
|
||||
chatId: e.chatId,
|
||||
customTitle: e.title,
|
||||
teamId,
|
||||
teamToken
|
||||
});
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{RenderHistoryList}
|
||||
{/* chat container */}
|
||||
<Flex
|
||||
position={'relative'}
|
||||
|
Reference in New Issue
Block a user