From 8767c576be16a7cc700ed8d54ca65cb4e34c4069 Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Tue, 25 Apr 2023 21:06:04 +0800 Subject: [PATCH] feat: stop chat --- public/docs/versionIntro.md | 1 + src/api/fetch.ts | 34 ++++++++++++--------- src/components/Icon/icons/chatting.svg | 19 ------------ src/components/Icon/icons/stop.svg | 1 + src/components/Icon/index.tsx | 4 +-- src/pages/chat/index.module.scss | 11 +++++++ src/pages/chat/index.tsx | 42 +++++++++++++++----------- 7 files changed, 59 insertions(+), 53 deletions(-) delete mode 100644 src/components/Icon/icons/chatting.svg create mode 100644 src/components/Icon/icons/stop.svg create mode 100644 src/pages/chat/index.module.scss diff --git a/public/docs/versionIntro.md b/public/docs/versionIntro.md index 25345d437..ca022f29d 100644 --- a/public/docs/versionIntro.md +++ b/public/docs/versionIntro.md @@ -1,4 +1,5 @@ ### Fast GPT V2.8.1 +* 新增 - 暂停聊天。 * 优化 - 知识库升级,内容条数不上限! * 优化 - 导入去重效果,可防止导出后的 csv 重复导入。 * 优化 - 聊天框,电脑端复制删除图标。 diff --git a/src/api/fetch.ts b/src/api/fetch.ts index 92949af6a..a7822015b 100644 --- a/src/api/fetch.ts +++ b/src/api/fetch.ts @@ -6,7 +6,7 @@ interface StreamFetchProps { abortSignal: AbortController; } export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchProps) => - new Promise(async (resolve, reject) => { + new Promise(async (resolve, reject) => { try { const res = await fetch(url, { method: 'POST', @@ -23,26 +23,30 @@ export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchPr let responseText = ''; const read = async () => { - const { done, value } = await reader?.read(); - if (done) { - if (res.status === 200) { - resolve(responseText); - } else { - try { + try { + const { done, value } = await reader?.read(); + if (done) { + if (res.status === 200) { + resolve(responseText); + } else { const parseError = JSON.parse(responseText); reject(parseError?.message || '请求异常'); - } catch (err) { - reject('请求异常'); } - } - return; + return; + } + const text = decoder.decode(value).replace(//g, '\n'); + res.status === 200 && onMessage(text); + responseText += text; + read(); + } catch (err: any) { + if (err?.message === 'The user aborted a request.') { + return resolve(responseText); + } + reject(typeof err === 'string' ? err : err?.message || '请求异常'); } - const text = decoder.decode(value).replace(//g, '\n'); - res.status === 200 && onMessage(text); - responseText += text; - read(); }; + read(); } catch (err: any) { console.log(err, '===='); diff --git a/src/components/Icon/icons/chatting.svg b/src/components/Icon/icons/chatting.svg deleted file mode 100644 index 32edead6e..000000000 --- a/src/components/Icon/icons/chatting.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/components/Icon/icons/stop.svg b/src/components/Icon/icons/stop.svg new file mode 100644 index 000000000..915cfbef8 --- /dev/null +++ b/src/components/Icon/icons/stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Icon/index.tsx b/src/components/Icon/index.tsx index 7db6b50bd..9f9895db2 100644 --- a/src/components/Icon/index.tsx +++ b/src/components/Icon/index.tsx @@ -13,12 +13,12 @@ const map = { board: require('./icons/board.svg').default, develop: require('./icons/develop.svg').default, user: require('./icons/user.svg').default, - chatting: require('./icons/chatting.svg').default, promotion: require('./icons/promotion.svg').default, delete: require('./icons/delete.svg').default, withdraw: require('./icons/withdraw.svg').default, dbModel: require('./icons/dbModel.svg').default, - history: require('./icons/history.svg').default + history: require('./icons/history.svg').default, + stop: require('./icons/stop.svg').default }; export type IconName = keyof typeof map; diff --git a/src/pages/chat/index.module.scss b/src/pages/chat/index.module.scss new file mode 100644 index 000000000..f039eccdc --- /dev/null +++ b/src/pages/chat/index.module.scss @@ -0,0 +1,11 @@ +.stopIcon { + animation: zoomStopIcon 0.4s infinite alternate; +} +@keyframes zoomStopIcon { + 0% { + transform: scale(0.8); + } + 100% { + transform: scale(1.2); + } +} diff --git a/src/pages/chat/index.tsx b/src/pages/chat/index.tsx index 6580a1de6..80e62b6d3 100644 --- a/src/pages/chat/index.tsx +++ b/src/pages/chat/index.tsx @@ -36,6 +36,8 @@ const SlideBar = dynamic(() => import('./components/SlideBar')); const Empty = dynamic(() => import('./components/Empty')); const Markdown = dynamic(() => import('@/components/Markdown')); +import styles from './index.module.scss'; + const textareaMinH = '22px'; export type ChatSiteItemType = { @@ -202,8 +204,9 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => { obj: prompts.obj, value: prompts.value }; + // 流请求,获取数据 - const res = await streamFetch({ + const responseText = await streamFetch({ url: urlMap[chatData.modelName], data: { prompt, @@ -226,22 +229,22 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => { abortSignal }); - let id = ''; + let newChatId = ''; // 保存对话信息 try { - id = await postSaveChat({ + newChatId = await postSaveChat({ modelId, chatId, prompts: [ prompt, { obj: 'AI', - value: res as string + value: responseText } ] }); - if (id) { - router.replace(`/chat?modelId=${modelId}&chatId=${id}`); + if (newChatId) { + router.replace(`/chat?modelId=${modelId}&chatId=${newChatId}`); } } catch (err) { toast({ @@ -252,10 +255,10 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => { }); } - // 设置完成状态 + // 设置聊天内容为完成状态 setChatData((state) => ({ ...state, - chatId: id || state.chatId, // 如果有 Id,说明是新创建的对话 + chatId: newChatId || state.chatId, // 如果有 Id,说明是新创建的对话 history: state.history.map((item, index) => { if (index !== state.history.length - 1) return item; return { @@ -559,19 +562,23 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => { {isChatting ? ( { + controller.current?.abort(); + }} /> ) : ( { width={['18px', '20px']} height={['18px', '20px']} cursor={'pointer'} - fill={useColorModeValue('#718096', 'white')} - > + color={useColorModeValue('gray.500', 'white')} + onClick={sendPrompt} + /> )}