diff --git a/src/components/Markdown/codeLight.ts b/src/components/Markdown/codeLight.ts index d6f17fd10..460e35305 100644 --- a/src/components/Markdown/codeLight.ts +++ b/src/components/Markdown/codeLight.ts @@ -2,9 +2,7 @@ import React from 'react'; export const codeLight: { [key: string]: React.CSSProperties } = { 'code[class*=language-]': { color: '#d4d4d4', - fontSize: '13px', textShadow: 'none', - fontFamily: 'Menlo,Monaco,Consolas,"Andale Mono","Ubuntu Mono","Courier New",monospace', direction: 'ltr', textAlign: 'left', whiteSpace: 'pre', @@ -21,9 +19,7 @@ export const codeLight: { [key: string]: React.CSSProperties } = { }, 'pre[class*=language-]': { color: '#d4d4d4', - fontSize: '13px', textShadow: 'none', - fontFamily: 'Menlo,Monaco,Consolas,"Andale Mono","Ubuntu Mono","Courier New",monospace', direction: 'ltr', textAlign: 'left', whiteSpace: 'pre', diff --git a/src/components/Markdown/index.module.scss b/src/components/Markdown/index.module.scss index 8b89f1f5c..825544096 100644 --- a/src/components/Markdown/index.module.scss +++ b/src/components/Markdown/index.module.scss @@ -341,7 +341,7 @@ background-color: #f0f0f0; border: 1px solid #cccccc; border-radius: 3px 3px 3px 3px; - font-size: 13px; + font-size: max(0.9em, 14px); line-height: 19px; overflow: auto; padding: 6px 10px; @@ -352,11 +352,12 @@ border: medium none; } .markdown { - font-size: 14px; - line-height: 1.6; - letter-spacing: 0.5px; text-align: justify; word-break: break-all; + overflow-y: hidden; + tab-size: 4; + word-spacing: normal; + pre { display: block; width: 100%; @@ -372,7 +373,6 @@ background-color: #222 !important; color: #fff; width: 100%; - font-family: 'Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji'; } a { diff --git a/src/components/Markdown/index.tsx b/src/components/Markdown/index.tsx index 676281fac..654bc0245 100644 --- a/src/components/Markdown/index.tsx +++ b/src/components/Markdown/index.tsx @@ -1,8 +1,6 @@ import React, { memo, useMemo } from 'react'; import ReactMarkdown from 'react-markdown'; -import styles from './index.module.scss'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; -import { codeLight } from './codeLight'; import { Box, Flex } from '@chakra-ui/react'; import { useCopyData } from '@/utils/tools'; import Icon from '@/components/Icon'; @@ -10,8 +8,12 @@ import remarkGfm from 'remark-gfm'; import remarkMath from 'remark-math'; import rehypeKatex from 'rehype-katex'; +import 'katex/dist/katex.min.css'; +import styles from './index.module.scss'; +import { codeLight } from './codeLight'; + const Markdown = ({ source, isChatting }: { source: string; isChatting: boolean }) => { - const formatSource = useMemo(() => source.replace(/\n/g, ' \n'), [source]); + const formatSource = useMemo(() => source, [source]); const { copyData } = useCopyData(); return ( diff --git a/src/constants/theme.ts b/src/constants/theme.ts index 72661ab04..5c60060bc 100644 --- a/src/constants/theme.ts +++ b/src/constants/theme.ts @@ -20,24 +20,27 @@ const Button = defineStyleConfig({ baseStyle: {}, sizes: { sm: { - fontSize: 'sm', + fontSize: 'xs', px: 3, py: 0, fontWeight: 'normal', - height: '26px' + height: '26px', + lineHeight: '26px' }, md: { - fontSize: 'md', + fontSize: 'sm', px: 6, py: 0, height: '34px', + lineHeight: '34px', fontWeight: 'normal' }, lg: { - fontSize: 'lg', + fontSize: 'md', px: 8, py: 0, height: '42px', + lineHeight: '42px', fontWeight: 'normal' } }, @@ -58,17 +61,12 @@ export const theme = extendTheme({ global: { 'html, body': { color: 'blackAlpha.800', - fontSize: '14px', - fontFamily: - 'Söhne,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Ubuntu,Cantarell,Noto Sans,sans-serif,Helvetica Neue,Arial,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji', height: '100%', - overflowY: 'auto' + maxHeight: '100vh', + overflowY: 'hidden' } } }, - fonts: { - body: 'system-ui, sans-serif' - }, fontSizes: { xs: '0.8rem', sm: '0.9rem', diff --git a/src/hooks/useSendCode.ts b/src/hooks/useSendCode.ts index 8e683b5b7..71939a364 100644 --- a/src/hooks/useSendCode.ts +++ b/src/hooks/useSendCode.ts @@ -1,14 +1,11 @@ import { useState, useMemo, useCallback } from 'react'; import { sendCodeToEmail } from '@/api/user'; import { EmailTypeEnum } from '@/constants/common'; -import { useToast } from '@chakra-ui/react'; let timer: any; +import { useToast } from './useToast'; export const useSendCode = () => { - const toast = useToast({ - position: 'top', - duration: 2000 - }); + const { toast } = useToast(); const [codeSending, setCodeSending] = useState(false); const [codeCountDown, setCodeCountDown] = useState(0); const sendCodeText = useMemo(() => { @@ -43,13 +40,11 @@ export const useSendCode = () => { status: 'success', position: 'top' }); - } catch (error) { - typeof error === 'string' && - toast({ - title: error, - status: 'error', - position: 'top' - }); + } catch (error: any) { + toast({ + title: error.message || '发送验证码异常', + status: 'error' + }); } setCodeSending(false); }, diff --git a/src/pages/api/user/sendEmail.ts b/src/pages/api/user/sendEmail.ts index 6ff5ee2cb..4a2b727b7 100644 --- a/src/pages/api/user/sendEmail.ts +++ b/src/pages/api/user/sendEmail.ts @@ -20,7 +20,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) if (type === EmailTypeEnum.register) { const maxCount = process.env.MAX_USER ? +process.env.MAX_USER : Infinity; const userCount = await User.count(); - if (userCount >= maxCount) { throw new Error('当前注册用户已满,请等待名额~'); } diff --git a/src/pages/chat/index.tsx b/src/pages/chat/index.tsx index 9c5dc6d37..159e16e1c 100644 --- a/src/pages/chat/index.tsx +++ b/src/pages/chat/index.tsx @@ -22,11 +22,10 @@ const Markdown = dynamic(() => import('@/components/Markdown')); const textareaMinH = '22px'; -const Chat = () => { +const Chat = ({ chatId, windowId }: { chatId: string; windowId?: string }) => { const { toast } = useToast(); const router = useRouter(); const { isPc, media } = useScreen(); - const { chatId, windowId } = router.query as { chatId: string; windowId?: string }; const ChatBox = useRef(null); const TextareaDom = useRef(null); @@ -40,7 +39,6 @@ const Chat = () => { // 滚动到底部 const scrollToBottom = useCallback(() => { - // 滚动到底部 setTimeout(() => { ChatBox.current && ChatBox.current.scrollTo({ @@ -52,16 +50,14 @@ const Chat = () => { // 初始化聊天框 useQuery( - [chatId, windowId], + ['initData'], () => { - if (!chatId) return null; setLoading(true); return getInitChatSiteInfo(chatId, windowId); }, { - cacheTime: 5 * 60 * 1000, onSuccess(res) { - if (!res) return; + // 可能没有 windowId,给它设置一下 router.replace(`/chat?chatId=${chatId}&windowId=${res.windowId}`); setChatSiteData(res.chatSite); @@ -72,7 +68,6 @@ const Chat = () => { })) ); scrollToBottom(); - setLoading(false); }, onError(e: any) { toast({ @@ -81,11 +76,30 @@ const Chat = () => { isClosable: true, duration: 5000 }); + }, + onSettled() { setLoading(false); } } ); + // 重置输入内容 + const resetInputVal = useCallback((val: string) => { + setInputVal(val); + setTimeout(() => { + /* 回到最小高度 */ + if (TextareaDom.current) { + TextareaDom.current.style.height = + val === '' ? textareaMinH : `${TextareaDom.current.scrollHeight}px`; + } + }, 100); + }, []); + + // 重载对话 + const resetChat = useCallback(() => { + window.open(`/chat?chatId=${chatId}`, '_self'); + }, [chatId]); + // gpt3 方法 const gpt3ChatPrompt = useCallback( async (newChatList: ChatSiteItemType[]) => { @@ -210,16 +224,8 @@ const Chat = () => { // 插入内容 setChatList(newChatList); - setInputVal(''); - // 滚动到底部 - setTimeout(() => { - scrollToBottom(); - - /* 回到最小高度 */ - if (TextareaDom.current) { - TextareaDom.current.style.height = textareaMinH; - } - }, 100); + resetInputVal(''); + scrollToBottom(); const fnMap: { [key: string]: any } = { [OpenAiModelEnum.GPT35]: chatGPTPrompt, @@ -239,13 +245,13 @@ const Chat = () => { } } catch (err) { toast({ - title: typeof err === 'string' ? err : '聊天已过期', + title: typeof err === 'string' ? err : '聊天出错了~', status: 'warning', duration: 5000, isClosable: true }); - setInputVal(storeInput); + resetInputVal(storeInput); setChatList(newChatList.slice(0, newChatList.length - 2)); } @@ -256,6 +262,7 @@ const Chat = () => { gpt3ChatPrompt, inputVal, isChatting, + resetInputVal, scrollToBottom, toast ]); @@ -267,16 +274,10 @@ const Chat = () => { await delLastMessage(windowId); const val = chatList[chatList.length - 1].value; - setInputVal(val); + resetInputVal(val); setChatList(chatList.slice(0, -1)); - - setTimeout(() => { - if (TextareaDom.current) { - TextareaDom.current.style.height = val.split('\n').length * 22 + 'px'; - } - }, 100); - }, [chatList, windowId]); + }, [chatList, resetInputVal, windowId]); return ( @@ -290,13 +291,9 @@ const Chat = () => { zIndex={1} > {chatSiteData?.name} - {/* 重置按键 */} - router.replace(`/chat?chatId=${chatId}`)}> - - {/* 滚动到底部按键 */} {ChatBox.current && ChatBox.current.scrollHeight > 2 * ChatBox.current.clientHeight && ( - + { > )} + {/* 重置按键 */} + {/* 聊天内容 */} @@ -312,7 +313,7 @@ const Chat = () => { @@ -321,11 +322,11 @@ const Chat = () => { /icon/logo.png - + {item.obj === 'AI' ? ( { 对话出现了异常 - @@ -428,3 +425,12 @@ const Chat = () => { }; export default Chat; + +export async function getServerSideProps(context: any) { + const chatId = context.query?.chatId || ''; + const windowId = context.query?.windowId || ''; + + return { + props: { chatId, windowId } + }; +} diff --git a/src/pages/model/components/ModelPhoneList.tsx b/src/pages/model/components/ModelPhoneList.tsx index 8d0913e89..a3e280876 100644 --- a/src/pages/model/components/ModelPhoneList.tsx +++ b/src/pages/model/components/ModelPhoneList.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { Box, Button, Flex, Heading, Tag } from '@chakra-ui/react'; +import React, { useEffect } from 'react'; +import { Box, Button, Flex, Tag } from '@chakra-ui/react'; import type { ModelType } from '@/types/model'; import { formatModelStatus } from '@/constants/model'; import dayjs from 'dayjs'; @@ -14,6 +14,10 @@ const ModelPhoneList = ({ }) => { const router = useRouter(); + useEffect(() => { + router.prefetch('/chat'); + }, [router]); + return ( {models.map((model) => ( diff --git a/src/pages/model/components/ModelTable.tsx b/src/pages/model/components/ModelTable.tsx index c87d0192d..963de509d 100644 --- a/src/pages/model/components/ModelTable.tsx +++ b/src/pages/model/components/ModelTable.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import { useEffect } from 'react'; import { Button, Table, @@ -84,6 +84,10 @@ const ModelTable = ({ } ]; + useEffect(() => { + router.prefetch('/chat'); + }, [router]); + return ( diff --git a/src/pages/model/detail.tsx b/src/pages/model/detail.tsx index 0116ed741..34d891199 100644 --- a/src/pages/model/detail.tsx +++ b/src/pages/model/detail.tsx @@ -49,7 +49,8 @@ const ModelDetail = () => { useEffect(() => { loadModel(); - }, [loadModel, modelId]); + router.prefetch('/chat'); + }, [loadModel, modelId, router]); /* 点击删除 */ const handleDelModel = useCallback(async () => { diff --git a/src/service/mongo.ts b/src/service/mongo.ts index 08d6e4599..9219e953e 100644 --- a/src/service/mongo.ts +++ b/src/service/mongo.ts @@ -11,6 +11,7 @@ export async function connectToDatabase(): Promise { global.mongodb = 'connecting'; console.log('connect mongo'); try { + mongoose.set('strictQuery', true); global.mongodb = await mongoose.connect(process.env.MONGODB_URI as string, { bufferCommands: true, dbName: 'doc_gpt', diff --git a/src/styles/reset.scss b/src/styles/reset.scss index c6b60dc7e..feb2a8810 100644 --- a/src/styles/reset.scss +++ b/src/styles/reset.scss @@ -50,6 +50,9 @@ svg { } @media (max-width: 900px) { + html { + font-size: 14px; + } ::-webkit-scrollbar, ::-webkit-scrollbar { width: 2px;