mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 13:03:50 +00:00
feat: history chat
This commit is contained in:
@@ -26,6 +26,17 @@ export const postGPT3SendPrompt = ({
|
||||
}))
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取历史记录
|
||||
*/
|
||||
export const getChatHistory = () =>
|
||||
GET<{ _id: string; title: string; modelId: string }[]>('/chat/getHistory');
|
||||
|
||||
/**
|
||||
* 删除一条历史记录
|
||||
*/
|
||||
export const delChatHistoryById = (id: string) => GET(`/chat/removeHistory?id=${id}`);
|
||||
|
||||
/**
|
||||
* 存储一轮对话
|
||||
*/
|
||||
|
1
src/components/Icon/icons/dbModel.svg
Normal file
1
src/components/Icon/icons/dbModel.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682232349111" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7070" xmlns:xlink="http://www.w3.org/1999/xlink" width="28" height="28"><path d="M512 102.6c110.7 0 215 12.3 293.9 34.7 35.8 10.2 65 22.1 84.5 34.7 18.6 12 21.3 19.7 21.6 20.6-0.2 0.9-3 8.6-21.6 20.6-19.5 12.5-48.7 24.5-84.5 34.7-78.9 22.3-183.2 34.7-293.9 34.7s-215-12.3-293.9-34.7c-35.8-10.2-65-22.1-84.5-34.7-18.6-12-21.3-19.7-21.6-20.6 0.2-0.9 3-8.6 21.6-20.6 19.5-12.5 48.7-24.5 84.5-34.7 78.9-22.4 183.2-34.7 293.9-34.7m0-40c-243 0-440 58.2-440 130s197 130 440 130 440-58.2 440-130-197-130-440-130zM112 190.4H72v641h40v-641z m840-0.3h-40v641h40v-641zM912 831v0.5c-0.2 0.9-3 8.6-21.6 20.6-19.5 12.5-48.7 24.5-84.5 34.7-78.9 22.3-183.2 34.6-293.9 34.6s-215-12.3-293.9-34.7c-35.8-10.2-65-22.1-84.5-34.7-18.6-12-21.3-19.7-21.6-20.6v-0.3l-40 0.3v0.1c0 71.8 197 130 440 130s440-58.2 440-130v-0.4l-40-0.1z m0-210.5v0.5c-0.2 0.9-3 8.6-21.6 20.6-19.5 12.5-48.7 24.5-84.5 34.7C727 698.6 622.7 711 512 711s-215-12.3-293.9-34.7c-35.8-10.2-65-22.1-84.5-34.7-18.6-12-21.3-19.7-21.6-20.6v-0.3l-40 0.3v0.1c0 71.8 197 130 440 130s440-58.2 440-130v-0.4l-40-0.2z m0-221.5v0.5c-0.2 0.9-3 8.6-21.6 20.6-19.5 12.5-48.7 24.5-84.5 34.7-78.9 22.3-183.2 34.7-293.9 34.7s-215-12.3-293.9-34.7c-35.8-10.2-65-22.1-84.5-34.7-18.6-12-21.3-19.7-21.6-20.6v-0.3l-40 0.3v0.1c0 71.8 197 130 440 130s440-58.2 440-130v-0.4l-40-0.2z" fill="" p-id="7071"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
1
src/components/Icon/icons/history.svg
Normal file
1
src/components/Icon/icons/history.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1682232686576" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8959" xmlns:xlink="http://www.w3.org/1999/xlink" width="28" height="28"><path d="M762.805186 140.938939c-14.335497-9.66922-33.725102-5.887081-43.373857 8.398274-9.648754 14.295588-5.897314 33.714869 8.398274 43.373857 106.369609 71.852468 169.864736 191.267185 169.864736 319.445496 0 212.414831-172.802648 385.217479-385.217479 385.217479S127.259382 724.571397 127.259382 512.156566c0-128.178311 63.494103-247.593028 169.864736-319.445496 14.295588-9.658987 18.047028-29.078269 8.398274-43.373857-9.658987-14.285355-29.088502-18.067494-43.373857-8.398274C138.575102 224.432539 64.791655 363.206162 64.791655 512.156566c0 246.851131 200.834074 447.685205 447.685205 447.685205S960.162066 759.007697 960.162066 512.156566C960.162066 363.206162 886.377596 224.432539 762.805186 140.938939z" p-id="8960"></path><path d="M401.003 64.47136c-17.253966 0-31.234375 13.980409-31.234375 31.233352l0 30.470989c0 17.253966 13.980409 31.234375 31.234375 31.234375s31.234375-13.980409 31.234375-31.234375L432.237375 95.704712C432.236352 78.450746 418.256966 64.47136 401.003 64.47136z" p-id="8961"></path><path d="M623.950721 64.47136c-17.253966 0-31.233352 13.980409-31.233352 31.233352l0 30.470989c0 17.253966 13.980409 31.234375 31.233352 31.234375s31.234375-13.980409 31.234375-31.234375L655.185097 95.704712C655.184073 78.450746 641.204687 64.47136 623.950721 64.47136z" p-id="8962"></path><path d="M426.012603 227.493248c11.214413 18.047028 41.970904 48.589648 86.157265 48.589648 43.963281 0 75.105558-30.318516 86.574774-48.223305 9.222035-14.396895 5.03262-33.358759-9.242502-42.763966-14.304797-9.405207-33.593096-5.398964-43.159986 8.764618-0.132006 0.193405-13.614066 19.754926-34.172287 19.754926-19.989263 0-32.423457-18.098193-33.267685-19.36914-9.160637-14.427594-28.264741-18.799158-42.834574-9.770528C421.416935 193.584973 416.912341 212.841549 426.012603 227.493248z" p-id="8963"></path><path d="M510.781242 335.164502c-17.253966 0-31.233352 13.980409-31.233352 31.233352l0 208.225415c0 0.63445 0.149403 1.227967 0.187265 1.853208 0.067538 1.115404 0.148379 2.217505 0.333598 3.314489 0.168846 1.00898 0.416486 1.978051 0.679475 2.951215 0.258896 0.954745 0.529049 1.895163 0.87595 2.821255 0.36839 0.981351 0.801249 1.916653 1.26276 2.847861 0.431835 0.876973 0.880043 1.734504 1.393743 2.569522 0.532119 0.860601 1.115404 1.670036 1.727341 2.472308 0.610914 0.805342 1.235131 1.588171 1.926886 2.336208 0.688685 0.74292 1.424442 1.420349 2.181689 2.093684 0.741897 0.659009 1.484817 1.303692 2.298346 1.89721 0.899486 0.657986 1.850138 1.222851 2.819209 1.783623 0.544399 0.314155 1.00898 0.714268 1.577938 0.998747l208.225415 104.113219c4.484128 2.236947 9.252735 3.304256 13.94971 3.304256 11.44875 0 22.479991-6.334265 27.959795-17.274432 7.706519-15.433504 1.454118-34.192753-13.970176-41.909505l-190.961216-95.480608L542.015617 366.397854C542.015617 349.143888 528.035208 335.164502 510.781242 335.164502z" p-id="8964"></path></svg>
|
After Width: | Height: | Size: 3.1 KiB |
@@ -16,7 +16,9 @@ const map = {
|
||||
chatting: require('./icons/chatting.svg').default,
|
||||
promotion: require('./icons/promotion.svg').default,
|
||||
delete: require('./icons/delete.svg').default,
|
||||
withdraw: require('./icons/withdraw.svg').default
|
||||
withdraw: require('./icons/withdraw.svg').default,
|
||||
dbModel: require('./icons/dbModel.svg').default,
|
||||
history: require('./icons/history.svg').default
|
||||
};
|
||||
|
||||
export type IconName = keyof typeof map;
|
||||
|
@@ -18,6 +18,7 @@ export const ChatModelNameMap = {
|
||||
};
|
||||
|
||||
export type ModelConstantsData = {
|
||||
icon: 'model' | 'dbModel';
|
||||
name: string;
|
||||
model: `${ChatModelNameEnum}`;
|
||||
trainName: string; // 空字符串代表不能训练
|
||||
@@ -29,6 +30,7 @@ export type ModelConstantsData = {
|
||||
|
||||
export const modelList: ModelConstantsData[] = [
|
||||
{
|
||||
icon: 'model',
|
||||
name: 'chatGPT',
|
||||
model: ChatModelNameEnum.GPT35,
|
||||
trainName: '',
|
||||
@@ -38,6 +40,7 @@ export const modelList: ModelConstantsData[] = [
|
||||
price: 3
|
||||
},
|
||||
{
|
||||
icon: 'dbModel',
|
||||
name: '知识库',
|
||||
model: ChatModelNameEnum.VECTOR_GPT,
|
||||
trainName: 'vector',
|
||||
|
31
src/pages/api/chat/getHistory.ts
Normal file
31
src/pages/api/chat/getHistory.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { authToken } from '@/service/utils/tools';
|
||||
|
||||
/* 获取历史记录 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const userId = await authToken(req.headers.authorization);
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
const data = await Chat.find(
|
||||
{
|
||||
userId
|
||||
},
|
||||
'_id title modelId'
|
||||
)
|
||||
.sort({ updateTime: -1 })
|
||||
.limit(20);
|
||||
|
||||
jsonRes(res, {
|
||||
data
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
27
src/pages/api/chat/removeHistory.ts
Normal file
27
src/pages/api/chat/removeHistory.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { ChatItemType } from '@/types/chat';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { authToken } from '@/service/utils/tools';
|
||||
|
||||
/* 获取历史记录 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { id } = req.query;
|
||||
const userId = await authToken(req.headers.authorization);
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
await Chat.findOneAndRemove({
|
||||
_id: id,
|
||||
userId
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
@@ -33,7 +33,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const { _id } = await Chat.create({
|
||||
userId,
|
||||
modelId,
|
||||
content
|
||||
content,
|
||||
title: content[0].value.slice(0, 20)
|
||||
});
|
||||
return jsonRes(res, {
|
||||
data: _id
|
||||
|
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useRef, useEffect } from 'react';
|
||||
import { AddIcon, ChatIcon, DeleteIcon, MoonIcon, SunIcon } from '@chakra-ui/icons';
|
||||
import {
|
||||
Box,
|
||||
@@ -16,14 +16,13 @@ import {
|
||||
useColorModeValue
|
||||
} from '@chakra-ui/react';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/router';
|
||||
import { getToken } from '@/utils/user';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { useCopyData } from '@/utils/tools';
|
||||
import WxConcat from '@/components/WxConcat';
|
||||
import { useMarkdown } from '@/hooks/useMarkdown';
|
||||
import { getChatHistory, delChatHistoryById } from '@/api/chat';
|
||||
import { modelList } from '@/constants/model';
|
||||
|
||||
const SlideBar = ({
|
||||
chatId,
|
||||
@@ -38,27 +37,34 @@ const SlideBar = ({
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
const { copyData } = useCopyData();
|
||||
const { myModels, getMyModels } = useUserStore();
|
||||
const { chatHistory, removeChatHistoryByWindowId } = useChatStore();
|
||||
const [hasReady, setHasReady] = useState(false);
|
||||
const { isOpen: isOpenShare, onOpen: onOpenShare, onClose: onCloseShare } = useDisclosure();
|
||||
const { isOpen: isOpenWx, onOpen: onOpenWx, onClose: onCloseWx } = useDisclosure();
|
||||
const { data: shareHint } = useMarkdown({ url: '/chatProblem.md' });
|
||||
const preChatId = useRef('chatId'); // 用于校验上一次chatId的情况,判断是否需要刷新历史记录
|
||||
|
||||
const { isSuccess } = useQuery(['init'], getMyModels, {
|
||||
const { isSuccess } = useQuery(['getMyModels'], getMyModels, {
|
||||
cacheTime: 5 * 60 * 1000
|
||||
});
|
||||
|
||||
const { data: chatHistory = [], mutate: loadChatHistory } = useMutation({
|
||||
mutationFn: getChatHistory
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setHasReady(true);
|
||||
}, []);
|
||||
if (chatId && preChatId.current === '') {
|
||||
loadChatHistory();
|
||||
}
|
||||
preChatId.current = chatId;
|
||||
}, [chatId, loadChatHistory]);
|
||||
|
||||
useEffect(() => {
|
||||
loadChatHistory();
|
||||
}, [loadChatHistory]);
|
||||
|
||||
const RenderHistory = () => (
|
||||
<>
|
||||
{chatHistory.map((item) => (
|
||||
<Flex
|
||||
key={item.chatId}
|
||||
key={item._id}
|
||||
alignItems={'center'}
|
||||
p={3}
|
||||
borderRadius={'md'}
|
||||
@@ -69,15 +75,16 @@ const SlideBar = ({
|
||||
}}
|
||||
fontSize={'xs'}
|
||||
border={'1px solid transparent'}
|
||||
{...(item.chatId === chatId
|
||||
{...(item._id === chatId
|
||||
? {
|
||||
borderColor: 'rgba(255,255,255,0.5)',
|
||||
backgroundColor: 'rgba(255,255,255,0.1)'
|
||||
}
|
||||
: {})}
|
||||
onClick={() => {
|
||||
if (item.chatId === chatId) return;
|
||||
resetChat(modelId, item.chatId);
|
||||
if (item._id === chatId) return;
|
||||
preChatId.current = 'chatId';
|
||||
resetChat(item.modelId, item._id);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
@@ -91,12 +98,14 @@ const SlideBar = ({
|
||||
variant={'unstyled'}
|
||||
aria-label={'edit'}
|
||||
size={'xs'}
|
||||
onClick={(e) => {
|
||||
removeChatHistoryByWindowId(item.chatId);
|
||||
if (item.chatId === chatId) {
|
||||
onClick={async (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
await delChatHistoryById(item._id);
|
||||
loadChatHistory();
|
||||
if (item._id === chatId) {
|
||||
resetChat();
|
||||
}
|
||||
e.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
@@ -151,53 +160,55 @@ const SlideBar = ({
|
||||
新对话
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{/* 我的模型 & 历史记录 折叠框*/}
|
||||
<Box flex={'1 0 0'} px={3} h={0} overflowY={'auto'}>
|
||||
<Accordion defaultIndex={[0]} allowMultiple>
|
||||
{isSuccess && (
|
||||
<AccordionItem borderTop={0} borderBottom={0}>
|
||||
<AccordionButton borderRadius={'md'} pl={1}>
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
其他模型
|
||||
</Box>
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel pb={4} px={0}>
|
||||
{myModels.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
alignItems={'center'}
|
||||
p={3}
|
||||
borderRadius={'md'}
|
||||
mb={2}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
backgroundColor: 'rgba(255,255,255,0.1)'
|
||||
}}
|
||||
fontSize={'xs'}
|
||||
border={'1px solid transparent'}
|
||||
{...(item._id === modelId
|
||||
? {
|
||||
borderColor: 'rgba(255,255,255,0.5)',
|
||||
backgroundColor: 'rgba(255,255,255,0.1)'
|
||||
}
|
||||
: {})}
|
||||
onClick={async () => {
|
||||
if (item._id === modelId) return;
|
||||
resetChat(item._id);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<MyIcon name="model" mr={2} fill={'white'} w={'16px'} h={'16px'} />
|
||||
<Box className={'textEllipsis'} flex={'1 0 0'} w={0}>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
)}
|
||||
{isSuccess && (
|
||||
<>
|
||||
<Box>
|
||||
{myModels.map((item) => (
|
||||
<Flex
|
||||
key={item._id}
|
||||
alignItems={'center'}
|
||||
p={3}
|
||||
borderRadius={'md'}
|
||||
mb={2}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
backgroundColor: 'rgba(255,255,255,0.1)'
|
||||
}}
|
||||
fontSize={'xs'}
|
||||
border={'1px solid transparent'}
|
||||
{...(item._id === modelId
|
||||
? {
|
||||
borderColor: 'rgba(255,255,255,0.5)',
|
||||
backgroundColor: 'rgba(255,255,255,0.1)'
|
||||
}
|
||||
: {})}
|
||||
onClick={async () => {
|
||||
if (item._id === modelId) return;
|
||||
resetChat(item._id);
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<MyIcon
|
||||
name={
|
||||
modelList.find((model) => model.model === item.service.modelName)?.icon ||
|
||||
'model'
|
||||
}
|
||||
mr={2}
|
||||
color={'white'}
|
||||
w={'16px'}
|
||||
h={'16px'}
|
||||
/>
|
||||
<Box className={'textEllipsis'} flex={'1 0 0'} w={0}>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
<Accordion allowToggle>
|
||||
<AccordionItem borderTop={0} borderBottom={0}>
|
||||
<AccordionButton borderRadius={'md'} pl={1}>
|
||||
<Box as="span" flex="1" textAlign="left">
|
||||
@@ -206,7 +217,7 @@ const SlideBar = ({
|
||||
<AccordionIcon />
|
||||
</AccordionButton>
|
||||
<AccordionPanel pb={0} px={0}>
|
||||
{hasReady && <RenderHistory />}
|
||||
<RenderHistory />
|
||||
</AccordionPanel>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
@@ -221,12 +232,6 @@ const SlideBar = ({
|
||||
</>
|
||||
</RenderButton>
|
||||
|
||||
{/* <RenderButton onClick={onOpenShare}>
|
||||
<>
|
||||
<MyIcon name="share" fill={'white'} w={'16px'} h={'16px'} mr={4} />
|
||||
分享
|
||||
</>
|
||||
</RenderButton> */}
|
||||
<RenderButton onClick={() => router.push('/number/setting')}>
|
||||
<>
|
||||
<MyIcon name="pay" fill={'white'} w={'16px'} h={'16px'} mr={4} />
|
||||
|
@@ -24,7 +24,6 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { ChatModelNameEnum } from '@/constants/model';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { useCopyData } from '@/utils/tools';
|
||||
import { streamFetch } from '@/api/fetch';
|
||||
import Icon from '@/components/Icon';
|
||||
@@ -72,7 +71,6 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
const { copyData } = useCopyData();
|
||||
const { isPc, media } = useScreen();
|
||||
const { setLoading } = useGlobalStore();
|
||||
const { pushChatHistory } = useChatStore();
|
||||
|
||||
// 滚动到底部
|
||||
const scrollToBottom = useCallback((behavior: 'smooth' | 'auto' = 'smooth') => {
|
||||
@@ -311,15 +309,6 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
|
||||
try {
|
||||
await gptChatPrompt(newChatList[newChatList.length - 2]);
|
||||
|
||||
// 如果是 Human 第一次发送,插入历史记录
|
||||
const humanChat = newChatList.filter((item) => item.obj === 'Human');
|
||||
if (humanChat.length === 1) {
|
||||
pushChatHistory({
|
||||
chatId,
|
||||
title: humanChat[0].value
|
||||
});
|
||||
}
|
||||
} catch (err: any) {
|
||||
toast({
|
||||
title: typeof err === 'string' ? err : err?.message || '聊天出错了~',
|
||||
@@ -335,17 +324,7 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
history: newChatList.slice(0, newChatList.length - 2)
|
||||
}));
|
||||
}
|
||||
}, [
|
||||
isChatting,
|
||||
inputVal,
|
||||
chatData.history,
|
||||
resetInputVal,
|
||||
toast,
|
||||
scrollToBottom,
|
||||
gptChatPrompt,
|
||||
pushChatHistory,
|
||||
chatId
|
||||
]);
|
||||
}, [isChatting, inputVal, chatData.history, resetInputVal, toast, scrollToBottom, gptChatPrompt]);
|
||||
|
||||
// 删除一句话
|
||||
const delChatRecord = useCallback(
|
||||
|
@@ -26,6 +26,10 @@ const ChatSchema = new Schema({
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '历史记录'
|
||||
},
|
||||
content: {
|
||||
type: [
|
||||
{
|
||||
|
@@ -1,50 +0,0 @@
|
||||
import { create } from 'zustand';
|
||||
import { devtools, persist } from 'zustand/middleware';
|
||||
import { immer } from 'zustand/middleware/immer';
|
||||
import type { HistoryItem } from '@/types/chat';
|
||||
|
||||
type Props = {
|
||||
chatHistory: HistoryItem[];
|
||||
pushChatHistory: (e: HistoryItem) => void;
|
||||
updateChatHistory: (chatId: string, title: string) => void;
|
||||
removeChatHistoryByWindowId: (chatId: string) => void;
|
||||
clearHistory: () => void;
|
||||
};
|
||||
export const useChatStore = create<Props>()(
|
||||
devtools(
|
||||
persist(
|
||||
immer((set, get) => ({
|
||||
chatHistory: [],
|
||||
pushChatHistory(item: HistoryItem) {
|
||||
set((state) => {
|
||||
if (state.chatHistory.find((history) => history.chatId === item.chatId)) return;
|
||||
state.chatHistory = [item, ...state.chatHistory].slice(0, 20);
|
||||
});
|
||||
},
|
||||
updateChatHistory(chatId: string, title: string) {
|
||||
set((state) => {
|
||||
state.chatHistory = state.chatHistory.map((item) => ({
|
||||
...item,
|
||||
title: item.chatId === chatId ? title : item.title
|
||||
}));
|
||||
});
|
||||
},
|
||||
removeChatHistoryByWindowId(chatId: string) {
|
||||
set((state) => {
|
||||
state.chatHistory = state.chatHistory.filter((item) => item.chatId !== chatId);
|
||||
});
|
||||
},
|
||||
clearHistory() {
|
||||
set((state) => {
|
||||
state.chatHistory = [];
|
||||
});
|
||||
}
|
||||
})),
|
||||
{
|
||||
name: 'chatHistory'
|
||||
// serialize: JSON.stringify,
|
||||
// deserialize: (data) => (data ? JSON.parse(data) : []),
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
Reference in New Issue
Block a user