From 877aab858be9fbd1b3871eea0aeb34a54ccf3b01 Mon Sep 17 00:00:00 2001 From: archer <545436317@qq.com> Date: Fri, 14 Jul 2023 18:27:08 +0800 Subject: [PATCH] fix: phone ui --- client/src/components/ChatBox/index.tsx | 20 +- .../src/components/Icon/icons/file/html.svg | 1 + .../components/Icon/icons/file/markdown.svg | 1 + client/src/components/Icon/icons/file/pdf.svg | 1 + client/src/components/Icon/index.tsx | 5 +- client/src/components/Layout/index.tsx | 59 +++--- client/src/pages/_app.tsx | 3 +- .../src/pages/chat/components/ChatHeader.tsx | 60 ++++++ .../chat/components/ChatHistorySlider.tsx | 30 ++- .../src/pages/chat/components/SliderApps.tsx | 2 +- client/src/pages/chat/components/ToolMenu.tsx | 33 +++- client/src/pages/chat/index.tsx | 72 +++----- client/src/pages/chat/share.tsx | 172 +++++++----------- client/src/store/chat.ts | 10 +- 14 files changed, 259 insertions(+), 210 deletions(-) create mode 100644 client/src/components/Icon/icons/file/html.svg create mode 100644 client/src/components/Icon/icons/file/markdown.svg create mode 100644 client/src/components/Icon/icons/file/pdf.svg create mode 100644 client/src/pages/chat/components/ChatHeader.tsx diff --git a/client/src/components/ChatBox/index.tsx b/client/src/components/ChatBox/index.tsx index 5946ce462..353833a03 100644 --- a/client/src/components/ChatBox/index.tsx +++ b/client/src/components/ChatBox/index.tsx @@ -312,8 +312,8 @@ const ChatBox = ( }; const controlContainerStyle = { className: 'control', - display: isChatting ? 'none' : ['flex', 'none'], color: 'myGray.400', + display: ['flex', 'none'], pl: 1, mt: 2, position: 'absolute' as any, @@ -321,12 +321,17 @@ const ChatBox = ( w: '100%' }; + const hasVariableInput = useMemo( + () => variableModules || welcomeText, + [variableModules, welcomeText] + ); + return ( - - + + {/* variable input */} - {(variableModules || welcomeText) && ( + {hasVariableInput && ( {/* avatar */} )} + {/* empty guide */} + {/* chat history */} - + {chatHistory.map((item, index) => ( {item.value} - + + {/* input */} {variableIsFinish ? ( \ No newline at end of file diff --git a/client/src/components/Icon/icons/file/markdown.svg b/client/src/components/Icon/icons/file/markdown.svg new file mode 100644 index 000000000..bc4b63601 --- /dev/null +++ b/client/src/components/Icon/icons/file/markdown.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/Icon/icons/file/pdf.svg b/client/src/components/Icon/icons/file/pdf.svg new file mode 100644 index 000000000..3767414c6 --- /dev/null +++ b/client/src/components/Icon/icons/file/pdf.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/Icon/index.tsx b/client/src/components/Icon/index.tsx index ce3273654..0b4f730ff 100644 --- a/client/src/components/Icon/index.tsx +++ b/client/src/components/Icon/index.tsx @@ -51,7 +51,10 @@ const map = { variable: require('./icons/modules/variable.svg').default, setTop: require('./icons/light/setTop.svg').default, fullScreenLight: require('./icons/light/fullScreen.svg').default, - voice: require('./icons/voice.svg').default + voice: require('./icons/voice.svg').default, + html: require('./icons/file/html.svg').default, + pdf: require('./icons/file/pdf.svg').default, + markdown: require('./icons/file/markdown.svg').default }; export type IconName = keyof typeof map; diff --git a/client/src/components/Layout/index.tsx b/client/src/components/Layout/index.tsx index f81c690c7..8a64c8147 100644 --- a/client/src/components/Layout/index.tsx +++ b/client/src/components/Layout/index.tsx @@ -63,34 +63,39 @@ const Layout = ({ children }: { children: JSX.Element }) => { return ( <> - - {pcUnShowLayoutRoute[router.pathname] ? ( - {children} - ) : ( - <> - - - - + {isPc ? ( + <> + {pcUnShowLayoutRoute[router.pathname] ? ( + {children} + ) : ( + <> + + + + + {children} + + + )} + + ) : ( + <> + + {phoneUnShowLayoutRoute[router.pathname] || isChatPage ? ( {children} - - - )} - - - {phoneUnShowLayoutRoute[router.pathname] || isChatPage ? ( - {children} - ) : ( - - - {children} - - - - - - )} - + ) : ( + + + {children} + + + + + + )} + + + )} diff --git a/client/src/pages/_app.tsx b/client/src/pages/_app.tsx index 4f6617cb6..993c6421d 100644 --- a/client/src/pages/_app.tsx +++ b/client/src/pages/_app.tsx @@ -44,7 +44,7 @@ function App({ Component, pageProps }: AppProps) { <> Fast GPT - + - {/* @ts-ignore */} diff --git a/client/src/pages/chat/components/ChatHeader.tsx b/client/src/pages/chat/components/ChatHeader.tsx new file mode 100644 index 000000000..44bbc8f44 --- /dev/null +++ b/client/src/pages/chat/components/ChatHeader.tsx @@ -0,0 +1,60 @@ +import React from 'react'; +import { Flex, useTheme, Box } from '@chakra-ui/react'; +import { useGlobalStore } from '@/store/global'; +import MyIcon from '@/components/Icon'; +import Tag from '@/components/Tag'; +import Avatar from '@/components/Avatar'; +import ToolMenu from './ToolMenu'; +import { ChatItemType } from '@/types/chat'; + +const ChatHeader = ({ + title, + history, + appAvatar, + onOpenSlider +}: { + title: string; + history: ChatItemType[]; + appAvatar: string; + onOpenSlider: () => void; +}) => { + const theme = useTheme(); + const { isPc } = useGlobalStore(); + return ( + + {isPc ? ( + <> + + {title} + + + + {history.length}条记录 + + + + ) : ( + <> + + + + + {title} + + + + )} + {/* control */} + + + ); +}; + +export default ChatHeader; diff --git a/client/src/pages/chat/components/ChatHistorySlider.tsx b/client/src/pages/chat/components/ChatHistorySlider.tsx index c4223c465..f3c21c05e 100644 --- a/client/src/pages/chat/components/ChatHistorySlider.tsx +++ b/client/src/pages/chat/components/ChatHistorySlider.tsx @@ -7,7 +7,8 @@ import { Menu, MenuButton, MenuList, - MenuItem + MenuItem, + IconButton } from '@chakra-ui/react'; import { useGlobalStore } from '@/store/global'; import { useRouter } from 'next/router'; @@ -29,8 +30,7 @@ const ChatHistorySlider = ({ activeHistoryId, onChangeChat, onDelHistory, - onSetHistoryTop, - onCloseSlider + onSetHistoryTop }: { appId?: string; appName: string; @@ -40,7 +40,6 @@ const ChatHistorySlider = ({ onChangeChat: (historyId?: string) => void; onDelHistory: (historyId: string) => void; onSetHistoryTop?: (e: { historyId: string; top: boolean }) => void; - onCloseSlider: () => void; }) => { const theme = useTheme(); const router = useRouter(); @@ -180,6 +179,29 @@ const ChatHistorySlider = ({ ))} + + {!isPc && appId && ( + router.push('/app/list')} + > + } + bg={'white'} + boxShadow={'1px 1px 9px rgba(0,0,0,0.15)'} + h={'28px'} + size={'sm'} + borderRadius={'50%'} + aria-label={''} + /> + 切换应用 + + )} ); }; diff --git a/client/src/pages/chat/components/SliderApps.tsx b/client/src/pages/chat/components/SliderApps.tsx index 091c9f8b1..24949645f 100644 --- a/client/src/pages/chat/components/SliderApps.tsx +++ b/client/src/pages/chat/components/SliderApps.tsx @@ -21,7 +21,7 @@ const SliderApps = ({ appId }: { appId: string }) => { px={3} borderRadius={'md'} _hover={{ bg: 'myGray.200' }} - onClick={() => router.back()} + onClick={() => router.push('/app/list')} > { const { onExportChat } = useChatBox(); + const menuList = useRef([ + { + icon: 'shareLight', + label: '分享对话', + onClick: () => {} + }, + { + icon: 'apiLight', + label: 'HTML导出', + onClick: () => onExportChat({ type: 'html', history }) + }, + { + icon: 'markdown', + label: 'Markdown导出', + onClick: () => onExportChat({ type: 'md', history }) + }, + { icon: 'pdf', label: 'PDF导出', onClick: () => onExportChat({ type: 'pdf', history }) } + ]); return ( { > - - onExportChat({ type: 'html', history })}>导出HTML格式 - onExportChat({ type: 'pdf', history })}>导出PDF格式 - onExportChat({ type: 'md', history })}>导出Markdown格式 + + {menuList.current.map((item) => ( + + + {item.label} + + ))} ); diff --git a/client/src/pages/chat/index.tsx b/client/src/pages/chat/index.tsx index ce32f130f..dc6a9f1cb 100644 --- a/client/src/pages/chat/index.tsx +++ b/client/src/pages/chat/index.tsx @@ -1,11 +1,6 @@ import React, { useCallback, useState, useRef } from 'react'; import { useRouter } from 'next/router'; -import { - getInitChatSiteInfo, - delChatRecordByIndex, - delChatHistoryById, - putChatHistory -} from '@/api/chat'; +import { getInitChatSiteInfo, delChatRecordByIndex, putChatHistory } from '@/api/chat'; import { Box, Flex, @@ -39,6 +34,7 @@ import ChatHistorySlider from './components/ChatHistorySlider'; import SliderApps from './components/SliderApps'; import Tag from '@/components/Tag'; import ToolMenu from './components/ToolMenu'; +import ChatHeader from './components/ChatHeader'; const Chat = () => { const router = useRouter(); @@ -59,6 +55,7 @@ const Chat = () => { history, loadHistory, updateHistory, + delHistory, chatData, setChatData } = useChatStore(); @@ -138,14 +135,6 @@ const Chat = () => { }, [historyId, setChatData] ); - // delete a history - const delHistoryById = useCallback( - async (historyId: string) => { - await delChatHistoryById(historyId); - loadHistory({ appId }); - }, - [appId, loadHistory] - ); // get chat app info const loadChatInfo = useCallback( @@ -232,7 +221,7 @@ const Chat = () => { useQuery(['loadHistory', appId], () => (appId ? loadHistory({ appId }) : null)); return ( - + {/* pc show myself apps */} {isPc && ( @@ -270,15 +259,22 @@ const Chat = () => { appId } }); + if (!isPc) { + onCloseSlider(); + } }} - onDelHistory={delHistoryById} + onDelHistory={delHistory} onSetHistoryTop={async (e) => { try { await putChatHistory(e); - loadHistory({ appId }); + const historyItem = history.find((item) => item._id === e.historyId); + if (!historyItem) return; + updateHistory({ + ...historyItem, + top: e.top + }); } catch (error) {} }} - onCloseSlider={onCloseSlider} /> )} {/* chat container */} @@ -289,38 +285,14 @@ const Chat = () => { flex={'1 0 0'} flexDirection={'column'} > - - {isPc ? ( - <> - - {chatData.title} - - - - {chatData.history.length}条记录 - - - ) : ( - <> - - - )} - - - + {/* header */} + + {/* chat box */} { - const theme = useTheme(); +const ShareChat = ({ shareId, historyId }: { shareId: string; historyId: string }) => { const router = useRouter(); - const { shareId = '', historyId } = router.query as { shareId: string; historyId: string }; const { toast } = useToast(); const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure(); const { isPc } = useGlobalStore(); @@ -48,8 +36,6 @@ const ShareChat = () => { const startChat = useCallback( async ({ messages, controller, generatingMessage, variables }: StartChatFnProps) => { - console.log(messages, variables); - const prompts = messages.slice(-shareChatData.maxContext - 2); const { responseText } = await streamFetch({ data: { @@ -103,8 +89,8 @@ const ShareChat = () => { ); const loadAppInfo = useCallback( - async (shareId?: string) => { - if (!shareId) return null; + async (shareId: string, historyId: string) => { + if (!shareId || !historyId) return null; const history = shareChatHistory.find((item) => item._id === historyId) || defaultHistory; ChatBoxRef.current?.resetHistory(history.chats); @@ -140,74 +126,52 @@ const ShareChat = () => { return history; }, - [ - delManyShareChatHistoryByShareId, - historyId, - setShareChatData, - shareChatData, - shareChatHistory, - toast - ] + [delManyShareChatHistoryByShareId, setShareChatData, shareChatData, shareChatHistory, toast] ); - useQuery(['init', shareId, historyId], () => { - return loadAppInfo(shareId); - }); + useEffect(() => { + loadAppInfo(shareId, historyId); + }, [shareId, historyId]); return ( - {/* slider */} - {isPc ? ( - - item.shareId === shareId) - .map((item) => ({ - id: item._id, - title: item.title - }))} - onChangeChat={(historyId) => { - router.push({ - query: { - historyId: historyId || '', - shareId - } - }); - }} - onDelHistory={delOneShareHistoryByHistoryId} - onCloseSlider={onCloseSlider} - /> - - ) : ( - - - - ({ - id: item._id, - title: item.title - }))} - onChangeChat={(historyId) => { - router.push({ - query: { - historyId: historyId || '', - shareId - } - }); - }} - onDelHistory={delOneShareHistoryByHistoryId} - onCloseSlider={onCloseSlider} - /> - - + {((children: React.ReactNode) => { + return isPc ? ( + {children} + ) : ( + + + + {children} + + + ); + })( + ({ + id: item._id, + title: item.title + }))} + onChangeChat={(historyId) => { + router.push({ + query: { + historyId: historyId || '', + shareId + } + }); + if (!isPc) { + onCloseSlider(); + } + }} + onDelHistory={delOneShareHistoryByHistoryId} + onCloseSlider={onCloseSlider} + /> )} + {/* chat container */} { flex={'1 0 0'} flexDirection={'column'} > - - {isPc ? ( - <> - - {shareChatData.history.title} - - - - {shareChatData.history.chats.length}条记录 - - - ) : ( - <> - - - )} - + {/* header */} + {/* chat box */} { ); }; +export async function getServerSideProps(context: any) { + const shareId = context?.query?.shareId || ''; + const historyId = context?.query?.historyId || ''; + + return { + props: { shareId, historyId } + }; +} + export default ShareChat; diff --git a/client/src/store/chat.ts b/client/src/store/chat.ts index e901b82de..8ebf16680 100644 --- a/client/src/store/chat.ts +++ b/client/src/store/chat.ts @@ -4,12 +4,12 @@ import { immer } from 'zustand/middleware/immer'; import { ChatHistoryItemType } from '@/types/chat'; import type { InitChatResponse } from '@/api/response/chat'; -import { getChatHistory } from '@/api/chat'; -import { HUMAN_ICON } from '@/constants/chat'; +import { delChatHistoryById, getChatHistory } from '@/api/chat'; type State = { history: ChatHistoryItemType[]; loadHistory: (data: { appId?: string }) => Promise; + delHistory(history: string): Promise; updateHistory: (history: ChatHistoryItemType) => void; chatData: InitChatResponse; setChatData: (e: InitChatResponse | ((e: InitChatResponse) => InitChatResponse)) => void; @@ -63,6 +63,12 @@ export const useChatStore = create()( }); return null; }, + async delHistory(historyId) { + set((state) => { + state.history = state.history.filter((item) => item._id !== historyId); + }); + await delChatHistoryById(historyId); + }, updateHistory(history) { const index = get().history.findIndex((item) => item._id === history._id); set((state) => {