This commit is contained in:
Archer 2023-03-15 21:36:56 +08:00
parent be69cfb966
commit 7529f51e72
No known key found for this signature in database
GPG Key ID: A3F5915562F98511
7 changed files with 196 additions and 172 deletions

View File

@ -45,8 +45,13 @@ const Button = defineStyleConfig({
} }
}, },
variants: { variants: {
outline: { white: {
borderWidth: '1.5px' color: '#fff',
backgroundColor: 'transparent',
border: '1px solid #ffffff',
_hover: {
backgroundColor: 'rgba(255,255,255,0.1)'
}
} }
}, },
defaultProps: { defaultProps: {

View File

@ -103,9 +103,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
} }
}; };
const decoder = new TextDecoder();
try {
for await (const chunk of chatResponse.data as any) { for await (const chunk of chatResponse.data as any) {
const parser = createParser(onParse); const parser = createParser(onParse);
parser.feed(decodeURIComponent(chunk)); parser.feed(decoder.decode(chunk));
}
} catch (error) {
console.log('pipe error', error);
} }
pass.push(null); pass.push(null);
} catch (err: any) { } catch (err: any) {

View File

@ -0,0 +1,19 @@
import React from 'react';
import { Box, Button } from '@chakra-ui/react';
import { AddIcon } from '@chakra-ui/icons';
const SlideBar = ({ resetChat }: { resetChat: () => void }) => {
return (
<Box flex={'0 0 250px'} p={3} backgroundColor={'blackAlpha.800'} color={'white'}>
{/* 新对话 */}
<Button w={'100%'} variant={'white'} h={'40px'} leftIcon={<AddIcon />} onClick={resetChat}>
</Button>
{/* 我的模型 */}
{/* 历史记录 */}
</Box>
);
};
export default SlideBar;

View File

@ -12,12 +12,21 @@ import { OpenAiModelEnum } from '@/constants/model';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useGlobalStore } from '@/store/global'; import { useGlobalStore } from '@/store/global';
import { streamFetch } from '@/api/fetch'; import { streamFetch } from '@/api/fetch';
import SlideBar from './components/SlideBar';
const Markdown = dynamic(() => import('@/components/Markdown')); const Markdown = dynamic(() => import('@/components/Markdown'));
const textareaMinH = '22px'; const textareaMinH = '22px';
const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => { const Chat = ({
chatId,
windowId,
timeStamp
}: {
chatId: string;
windowId?: string;
timeStamp: string;
}) => {
const { toast } = useToast(); const { toast } = useToast();
const router = useRouter(); const router = useRouter();
const { isPc, media } = useScreen(); const { isPc, media } = useScreen();
@ -45,7 +54,7 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
// 初始化聊天框 // 初始化聊天框
useQuery( useQuery(
['initData'], ['initData', timeStamp],
() => { () => {
setLoading(true); setLoading(true);
return getInitChatSiteInfo(chatId, windowId); return getInitChatSiteInfo(chatId, windowId);
@ -53,7 +62,7 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
{ {
onSuccess(res) { onSuccess(res) {
// 可能没有 windowId给它设置一下 // 可能没有 windowId给它设置一下
router.replace(`/chat?chatId=${chatId}&windowId=${res.windowId}`); router.replace(`/chat?chatId=${chatId}&windowId=${res.windowId}&timeStamp=${timeStamp}`);
setChatSiteData(res.chatSite); setChatSiteData(res.chatSite);
setChatList( setChatList(
@ -92,8 +101,8 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
// 重载对话 // 重载对话
const resetChat = useCallback(() => { const resetChat = useCallback(() => {
window.open(`/chat?chatId=${chatId}`, '_self'); router.push(`/chat?chatId=${chatId}&timeStamp=${Date.now()}`);
}, [chatId]); }, [chatId, router]);
// gpt3 方法 // gpt3 方法
const gpt3ChatPrompt = useCallback( const gpt3ChatPrompt = useCallback(
@ -270,35 +279,11 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
}, [chatList, resetInputVal, windowId]); }, [chatList, resetInputVal, windowId]);
return ( return (
<Flex height={'100%'} flexDirection={'column'}> <Flex h={'100%'}>
{/* 头部 */} <SlideBar resetChat={resetChat} />
<Flex <Flex flex={1} h={'100%'} flexDirection={'column'}>
px={4}
h={'50px'}
alignItems={'center'}
backgroundColor={'white'}
boxShadow={'0 5px 10px rgba(0,0,0,0.1)'}
zIndex={1}
>
<Box flex={1}>{chatSiteData?.name}</Box>
{/* 滚动到底部按键 */}
{ChatBox.current && ChatBox.current.scrollHeight > 2 * ChatBox.current.clientHeight && (
<Box mr={10} cursor={'pointer'} onClick={scrollToBottom}>
<Icon
name={'icon-xiangxiazhankai-xianxingyuankuang'}
width={25}
height={25}
color={'#718096'}
></Icon>
</Box>
)}
{/* 重置按键 */}
<Button size={'sm'} colorScheme={'gray'} onClick={resetChat}>
</Button>
</Flex>
{/* 聊天内容 */} {/* 聊天内容 */}
<Box ref={ChatBox} flex={'1 0 0'} h={0} w={'100%'} px={0} pb={10} overflowY={'auto'}> <Box ref={ChatBox} flex={'1 0 0'} h={0} w={'100%'} overflowY={'auto'}>
{chatList.map((item, index) => ( {chatList.map((item, index) => (
<Box <Box
key={index} key={index}
@ -307,7 +292,7 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
backgroundColor={index % 2 === 0 ? 'rgba(247,247,248,1)' : '#fff'} backgroundColor={index % 2 === 0 ? 'rgba(247,247,248,1)' : '#fff'}
borderBottom={'1px solid rgba(0,0,0,0.1)'} borderBottom={'1px solid rgba(0,0,0,0.1)'}
> >
<Flex maxW={'800px'} m={'auto'} alignItems={'flex-start'}> <Flex maxW={'750px'} m={'auto'} alignItems={'flex-start'}>
<Box mr={media(4, 1)}> <Box mr={media(4, 1)}>
<Image <Image
src={item.obj === 'Human' ? '/icon/human.png' : '/icon/logo.png'} src={item.obj === 'Human' ? '/icon/human.png' : '/icon/logo.png'}
@ -330,20 +315,11 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
</Box> </Box>
))} ))}
</Box> </Box>
{/* 空内容提示 */} {/* 发送区 */}
{/* {
chatList.length === 0 && (
<>
<Card>
</Card>
</>
)
} */}
<Box <Box
m={media('20px auto', '0 auto')} m={media('20px auto', '0 auto')}
w={media('100vw', '100%')} w={media('100vw', '100%')}
maxW={media('800px', 'auto')} maxW={media('750px', 'auto')}
boxShadow={'0 -14px 30px rgba(255,255,255,0.6)'} boxShadow={'0 -14px 30px rgba(255,255,255,0.6)'}
borderTop={media('none', '1px solid rgba(0,0,0,0.1)')} borderTop={media('none', '1px solid rgba(0,0,0,0.1)')}
> >
@ -421,6 +397,7 @@ const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => {
)} )}
</Box> </Box>
</Flex> </Flex>
</Flex>
); );
}; };
@ -429,8 +406,9 @@ export default Chat;
export async function getServerSideProps(context: any) { export async function getServerSideProps(context: any) {
const chatId = context.query?.chatId || ''; const chatId = context.query?.chatId || '';
const windowId = context.query?.windowId || ''; const windowId = context.query?.windowId || '';
const timeStamp = context.query?.timeStamp || `${Date.now()}`;
return { return {
props: { chatId, windowId } props: { chatId, windowId, timeStamp }
}; };
} }

View File

@ -1,6 +1,5 @@
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react';
import { Box, Button, Flex, Card } from '@chakra-ui/react'; import { Box, Button, Flex, Card } from '@chakra-ui/react';
import { getMyModels } from '@/api/model';
import { getChatSiteId } from '@/api/chat'; import { getChatSiteId } from '@/api/chat';
import { ModelType } from '@/types/model'; import { ModelType } from '@/types/model';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
@ -11,6 +10,7 @@ import { useQuery } from '@tanstack/react-query';
import { useLoading } from '@/hooks/useLoading'; import { useLoading } from '@/hooks/useLoading';
import dynamic from 'next/dynamic'; import dynamic from 'next/dynamic';
import { useToast } from '@/hooks/useToast'; import { useToast } from '@/hooks/useToast';
import { useUserStore } from '@/store/user';
const CreateModel = dynamic(() => import('./components/CreateModel')); const CreateModel = dynamic(() => import('./components/CreateModel'));
@ -18,22 +18,20 @@ const ModelList = () => {
const { toast } = useToast(); const { toast } = useToast();
const { isPc } = useScreen(); const { isPc } = useScreen();
const router = useRouter(); const router = useRouter();
const [models, setModels] = useState<ModelType[]>([]); const { myModels, setMyModels, getMyModels } = useUserStore();
const [openCreateModel, setOpenCreateModel] = useState(false); const [openCreateModel, setOpenCreateModel] = useState(false);
const { Loading, setIsLoading } = useLoading(); const { Loading, setIsLoading } = useLoading();
/* 加载模型 */ /* 加载模型 */
const { isLoading } = useQuery(['loadModels'], () => getMyModels(), { const { isLoading } = useQuery(['loadModels'], getMyModels);
onSuccess(res) {
if (!res) return;
setModels(res);
}
});
/* 创建成功回调 */ /* 创建成功回调 */
const createModelSuccess = useCallback((data: ModelType) => { const createModelSuccess = useCallback(
setModels((state) => [data, ...state]); (data: ModelType) => {
}, []); setMyModels([data, ...myModels]);
},
[myModels, setMyModels]
);
/* 点前往聊天预览页 */ /* 点前往聊天预览页 */
const handlePreviewChat = useCallback( const handlePreviewChat = useCallback(
@ -74,9 +72,9 @@ const ModelList = () => {
{/* 表单 */} {/* 表单 */}
<Box mt={5} position={'relative'}> <Box mt={5} position={'relative'}>
{isPc ? ( {isPc ? (
<ModelTable models={models} handlePreviewChat={handlePreviewChat} /> <ModelTable models={myModels} handlePreviewChat={handlePreviewChat} />
) : ( ) : (
<ModelPhoneList models={models} handlePreviewChat={handlePreviewChat} /> <ModelPhoneList models={myModels} handlePreviewChat={handlePreviewChat} />
)} )}
</Box> </Box>
{/* 创建弹窗 */} {/* 创建弹窗 */}

View File

@ -28,7 +28,7 @@ export const jsonRes = (
} else if (openaiError[error?.response?.statusText]) { } else if (openaiError[error?.response?.statusText]) {
msg = openaiError[error.response.statusText]; msg = openaiError[error.response.statusText];
} }
// console.log(error?.response)
console.log('error->', error.code, error?.response?.statusText, msg); console.log('error->', error.code, error?.response?.statusText, msg);
} }

View File

@ -2,25 +2,30 @@ import { create } from 'zustand';
import { devtools } from 'zustand/middleware'; import { devtools } from 'zustand/middleware';
import { immer } from 'zustand/middleware/immer'; import { immer } from 'zustand/middleware/immer';
import type { UserType, UserUpdateParams } from '@/types/user'; import type { UserType, UserUpdateParams } from '@/types/user';
import type { ModelType } from '@/types/model';
import { setToken } from '@/utils/user'; import { setToken } from '@/utils/user';
import { getMyModels } from '@/api/model';
type State = { type State = {
userInfo: UserType | null; userInfo: UserType | null;
setUserInfo: (user: UserType, token?: string) => void; setUserInfo: (user: UserType, token?: string) => void;
updateUserInfo: (user: UserUpdateParams) => void; updateUserInfo: (user: UserUpdateParams) => void;
myModels: ModelType[];
getMyModels: () => void;
setMyModels: (data: ModelType[]) => void;
}; };
export const useUserStore = create<State>()( export const useUserStore = create<State>()(
devtools( devtools(
immer((set, get) => ({ immer((set, get) => ({
userInfo: null, userInfo: null,
setUserInfo: (user: UserType, token?: string) => { setUserInfo(user: UserType, token?: string) {
set((state) => { set((state) => {
state.userInfo = user; state.userInfo = user;
}); });
token && setToken(token); token && setToken(token);
}, },
updateUserInfo: (user: UserUpdateParams) => { updateUserInfo(user: UserUpdateParams) {
set((state) => { set((state) => {
if (!state.userInfo) return; if (!state.userInfo) return;
state.userInfo = { state.userInfo = {
@ -28,6 +33,20 @@ export const useUserStore = create<State>()(
...user ...user
}; };
}); });
},
myModels: [],
getMyModels: () =>
getMyModels().then((res) => {
set((state) => {
state.myModels = res;
});
return res;
}),
setMyModels(data: ModelType[]) {
set((state) => {
state.myModels = data;
});
return null;
} }
})) }))
) )