Compare commits

..

2 Commits
v0.9 ... v1.0

Author SHA1 Message Date
Archer
17364e9da3 conflict
perf: 聊天页优化

perf: md解析样式

perf: ui调整

perf: 懒加载和动态加载优化

perf: 去除console,

perf: 图片cdn

feat: 图片地址

perf: 登录顺序

feat: 流优化
2023-03-09 20:44:54 +08:00
archer
2390823282 feat: 限流配置 2023-03-04 13:30:20 +08:00
37 changed files with 773 additions and 468 deletions

4
.gitignore vendored
View File

@@ -34,6 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
public/trainData/
.vscode/
/public/trainData/
/.vscode/
platform.json

View File

@@ -1,5 +0,0 @@
{
"editor.formatOnType": true,
"editor.formatOnSave": true ,
"prettier.tabWidth": 2
}

View File

@@ -15,22 +15,62 @@ TOKEN_KEY=随便填一个用于生成和校验token
```bash
pnpm dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
## 部署
```bash
# 本地 docker 打包
docker build -t imageName .
docker push imageName
# 服务器拉取部署
docker pull imageName
docker stop doc-gpt || true
docker rm doc-gpt || true
# 运行时才把参数写入
docker run -d --network=host --name doc-gpt -e AXIOS_PROXY_HOST= -e AXIOS_PROXY_PORT= -e MAILE_CODE= -e TOKEN_KEY= -e MONGODB_URI= imageName
docker build -t imageName:tag .
docker push imageName:tag
```
服务器请准备好 docker mongonginx和代理。 镜像走本机的代理,所以用 hostport改成代理的端口clash一般都是7890。
```bash
# 服务器拉取部署, imageName 替换成镜像名
docker pull imageName:tag
# 获取本地旧镜像ID
OLD_IMAGE_ID=$(docker images imageName -f "dangling=true" -q)
docker stop doc-gpt || true
docker rm doc-gpt || true
docker run -d --network=host --name doc-gpt \
-e MAX_USER=50 \
-e AXIOS_PROXY_HOST=127.0.0.1 \
-e AXIOS_PROXY_PORT=7890 \
-e MY_MAIL=your email\
-e MAILE_CODE=your email code \
-e TOKEN_KEY=任意一个内容 \
-e MONGODB_URI="mongodb://aha:ROOT_root123@127.0.0.0:27017/?authSource=admin&readPreference=primary&appname=MongoDB%20Compass&ssl=false" \
imageName:tag
docker logs doc-gpt
# 删除本地旧镜像
if [ ! -z "$OLD_IMAGE_ID" ]; then
docker rmi $OLD_IMAGE_ID
fi
```
### docker 安装
```bash
# 安装docker
curl -sSL https://get.daocloud.io/docker | sh
sudo systemctl start docker
```
### mongo 安装
```bash
docker pull mongo:6.0.4
docker stop mongo
docker rm mongo
docker run -d --name mongo \
-e MONGO_INITDB_ROOT_USERNAME= \
-e MONGO_INITDB_ROOT_PASSWORD= \
-v /root/service/mongo:/data/db \
mongo:6.0.4
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
# 介绍页
@@ -70,4 +110,4 @@ Open [http://localhost:3000](http://localhost:3000) with your browser to see the
### 其他问题
还有其他问题,可以加我 wx拉个交流群大家一起聊聊。
![](/imgs/erweima.jpg)
![](/icon/erweima.jpg)

View File

@@ -6,7 +6,17 @@ const isDev = process.env.NODE_ENV === 'development';
const nextConfig = {
output: 'standalone',
reactStrictMode: false,
compress: true
compress: true,
images: {
remotePatterns: [
{
protocol: 'https',
hostname: 'docgpt-1301319986.cos.ap-shanghai.myqcloud.com',
port: '',
pathname: '/**'
}
]
}
};
module.exports = nextConfig;

View File

@@ -23,8 +23,6 @@
"axios": "^1.3.3",
"crypto": "^1.0.1",
"dayjs": "^1.11.7",
"eslint": "8.34.0",
"eslint-config-next": "13.1.6",
"formidable": "^2.1.1",
"framer-motion": "^9.0.6",
"hyperdown": "^2.4.29",
@@ -40,7 +38,9 @@
"react-hook-form": "^7.43.1",
"react-markdown": "^8.0.5",
"react-syntax-highlighter": "^15.5.0",
"rehype-katex": "^6.0.2",
"remark-gfm": "^3.0.1",
"remark-math": "^5.1.1",
"sass": "^1.58.3",
"sharp": "^0.31.3",
"tunnel": "^0.0.6",
@@ -58,6 +58,8 @@
"@types/react-syntax-highlighter": "^15.5.6",
"@types/tunnel": "^0.0.3",
"@types/uuid": "^9.0.1",
"eslint": "8.34.0",
"eslint-config-next": "13.1.6",
"husky": "^8.0.3",
"lint-staged": "^13.1.2",
"prettier": "^2.8.4"

612
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

BIN
public/icon/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

View File

@@ -1,5 +1,6 @@
import { GET, POST, DELETE } from './request';
import { ChatItemType, ChatSiteType, ChatSiteItemType } from '@/types/chat';
import axios from 'axios';
/**
* 获取一个聊天框的ID
@@ -56,7 +57,7 @@ export const postChatGptPrompt = ({
});
/* 获取 Chat 的 Event 对象,进行持续通信 */
export const getChatGPTSendEvent = (chatId: string, windowId: string) =>
new EventSource(`/api/chat/chatGpt?chatId=${chatId}&windowId=${windowId}`);
new EventSource(`/api/chat/chatGpt?chatId=${chatId}&windowId=${windowId}&date=${Date.now()}`);
/**
* 删除最后一句

View File

@@ -34,7 +34,7 @@ function responseSuccess(response: AxiosResponse<ResponseDataType>) {
*/
function checkRes(data: ResponseDataType) {
if (data === undefined) {
console.log(data, 'data is empty');
console.error(data, 'data is empty');
return Promise.reject('服务器异常');
} else if (data.code < 200 || data.code >= 400) {
return Promise.reject(data.message);
@@ -49,21 +49,20 @@ function responseError(err: any) {
console.error('请求错误', err);
if (!err) {
return Promise.reject('未知错误');
return Promise.reject({ message: '未知错误' });
}
if (typeof err === 'string') {
return Promise.reject(err);
return Promise.reject({ message: err });
}
if (err.response) {
// 有报错响应
const res = err.response;
/* token过期,判断请求token与本地是否相同若不同需要重发 */
if (res.data.code in TOKEN_ERROR_CODE) {
clearToken();
return Promise.reject('token过期重新登录');
return Promise.reject({ message: 'token过期重新登录' });
}
}
return Promise.reject('未知错误');
return Promise.reject(err);
}
/* 创建请求实例 */

View File

@@ -39,7 +39,7 @@ const Auth = ({ children }: { children: JSX.Element }) => {
}
},
onError(error) {
console.log(error);
console.error(error);
router.push('/login');
toast();
},

View File

@@ -34,7 +34,7 @@ const Navbar = ({
>
{/* logo */}
<Box pb={4}>
<Image src={'/logo.png'} width={'35'} height={'35'} alt=""></Image>
<Image src={'/icon/logo.png'} width={'35'} height={'35'} alt=""></Image>
</Box>
{/* 导航列表 */}
<Box flex={1}>
@@ -46,6 +46,7 @@ const Navbar = ({
alignItems={'center'}
justifyContent={'center'}
onClick={() =>
!item.activeLink.includes(router.pathname) &&
router.push(item.link, undefined, {
shallow: true
})

View File

@@ -48,7 +48,7 @@ const NavbarPhone = ({
<DrawerContent maxWidth={'50vw'}>
<DrawerBody p={4}>
<Box py={4}>
<Image src={'/logo.png'} margin={'auto'} w={'35'} h={'35'} alt=""></Image>
<Image src={'/icon/logo.png'} margin={'auto'} w={'35'} h={'35'} alt=""></Image>
</Box>
{navbarList.map((item) => (
<Flex

View File

@@ -1,5 +1,47 @@
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',
wordSpacing: 'normal',
wordBreak: 'normal',
lineHeight: '1.5',
MozTabSize: '4',
OTabSize: '4',
tabSize: '4',
WebkitHyphens: 'none',
MozHyphens: 'none',
msHyphens: 'none',
hyphens: 'none'
},
'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',
wordSpacing: 'normal',
wordBreak: 'normal',
lineHeight: '1.5',
MozTabSize: '4',
OTabSize: '4',
tabSize: '4',
WebkitHyphens: 'none',
MozHyphens: 'none',
msHyphens: 'none',
hyphens: 'none',
padding: '1em',
margin: '.5em 0',
overflow: 'auto',
background: '#1e1e1e'
},
'code[class*=language-] ::selection': {
textShadow: 'none',
background: '#264f78'

View File

@@ -107,7 +107,6 @@
font-size: 28px;
}
.markdown h2 {
border-bottom: 1px solid #cccccc;
color: #000000;
font-size: 24px;
}
@@ -329,7 +328,6 @@
border-radius: 3px 3px 3px 3px;
margin: 0 2px;
padding: 0 5px;
white-space: nowrap;
}
.markdown pre > code {
background: none repeat scroll 0 0 transparent;
@@ -376,4 +374,9 @@
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 {
text-decoration: underline;
color: var(--chakra-colors-blue-600);
}
}

View File

@@ -1,23 +1,26 @@
import React, { memo } from 'react';
import React, { memo, useMemo } from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
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';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
import rehypeKatex from 'rehype-katex';
const Markdown = ({ source, isChatting }: { source: string; isChatting: boolean }) => {
// const formatSource = useMemo(() => source.replace(/\n/g, '\n'), [source]);
const formatSource = useMemo(() => source.replace(/\n/g, ' \n'), [source]);
const { copyData } = useCopyData();
// console.log(source);
return (
<ReactMarkdown
className={`${styles.markdown} ${
isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''
}`}
rehypePlugins={[remarkGfm]}
remarkPlugins={[remarkMath]}
rehypePlugins={[remarkGfm, rehypeKatex]}
components={{
pre: 'div',
code({ node, inline, className, children, ...props }) {
@@ -55,8 +58,9 @@ const Markdown = ({ source, isChatting }: { source: string; isChatting: boolean
);
}
}}
linkTarget="_blank"
>
{source}
{formatSource}
</ReactMarkdown>
);
};

View File

@@ -38,6 +38,5 @@ export const introPage = `
* 分享链接应为http://docgpt.ahapocket.cn/chat?chatId=6402c9f64cb5d6283f764
### 其他问题
还有其他问题,可以加我 wx拉个交流群大家一起聊聊。
![](/imgs/erweima.jpg)
还有其他问题,可以加我 wx: YNyiqi,拉个交流群大家一起聊聊。
`;

View File

@@ -1,4 +1,4 @@
import { useState, useRef } from 'react';
import { useCallback, useRef } from 'react';
import {
AlertDialog,
AlertDialogBody,
@@ -17,45 +17,51 @@ export const useConfirm = ({ title = '提示', content }: { title?: string; cont
const cancelCb = useRef<any>();
return {
openConfirm: (confirm?: any, cancel?: any) => {
onOpen();
confirmCb.current = confirm;
cancelCb.current = cancel;
},
ConfirmChild: () => (
<AlertDialog isOpen={isOpen} leastDestructiveRef={cancelRef} onClose={onClose}>
<AlertDialogOverlay>
<AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold">
{title}
</AlertDialogHeader>
openConfirm: useCallback(
(confirm?: any, cancel?: any) => {
onOpen();
confirmCb.current = confirm;
cancelCb.current = cancel;
},
[onOpen]
),
ConfirmChild: useCallback(
() => (
<AlertDialog isOpen={isOpen} leastDestructiveRef={cancelRef} onClose={onClose}>
<AlertDialogOverlay>
<AlertDialogContent>
<AlertDialogHeader fontSize="lg" fontWeight="bold">
{title}
</AlertDialogHeader>
<AlertDialogBody>{content}</AlertDialogBody>
<AlertDialogBody>{content}</AlertDialogBody>
<AlertDialogFooter>
<Button
colorScheme={'gray'}
onClick={() => {
onClose();
typeof cancelCb.current === 'function' && cancelCb.current();
}}
>
</Button>
<Button
colorScheme="blue"
ml={3}
onClick={() => {
onClose();
typeof confirmCb.current === 'function' && confirmCb.current();
}}
>
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
<AlertDialogFooter>
<Button
colorScheme={'gray'}
onClick={() => {
onClose();
typeof cancelCb.current === 'function' && cancelCb.current();
}}
>
</Button>
<Button
colorScheme="blue"
ml={4}
onClick={() => {
onClose();
typeof confirmCb.current === 'function' && confirmCb.current();
}}
>
</Button>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialogOverlay>
</AlertDialog>
),
[content, isOpen, onClose, title]
)
};
};

View File

@@ -1,36 +1,33 @@
import { useState, memo } from 'react';
import { useState, useCallback } from 'react';
import { Spinner, Flex } from '@chakra-ui/react';
export const useLoading = (props?: { defaultLoading: boolean }) => {
const [isLoading, setIsLoading] = useState(props?.defaultLoading || false);
const Loading = ({
loading,
fixed = true
}: {
loading?: boolean;
fixed?: boolean;
}): JSX.Element | null => {
return isLoading || loading ? (
<Flex
position={fixed ? 'fixed' : 'absolute'}
zIndex={100}
backgroundColor={'rgba(255,255,255,0.5)'}
top={0}
left={0}
right={0}
bottom={0}
alignItems={'center'}
justifyContent={'center'}
>
<Spinner thickness="4px" speed="0.65s" emptyColor="gray.200" color="blue.500" size="xl" />
</Flex>
) : null;
};
const Loading = useCallback(
({ loading, fixed = true }: { loading?: boolean; fixed?: boolean }): JSX.Element | null => {
return isLoading || loading ? (
<Flex
position={fixed ? 'fixed' : 'absolute'}
zIndex={100}
backgroundColor={'rgba(255,255,255,0.5)'}
top={0}
left={0}
right={0}
bottom={0}
alignItems={'center'}
justifyContent={'center'}
>
<Spinner thickness="4px" speed="0.65s" emptyColor="gray.200" color="blue.500" size="xl" />
</Flex>
) : null;
},
[isLoading]
);
return {
isLoading,
setIsLoading,
Loading: memo(Loading)
Loading
};
};

View File

@@ -15,18 +15,18 @@ Router.events.on('routeChangeStart', () => NProgress.start());
Router.events.on('routeChangeComplete', () => NProgress.done());
Router.events.on('routeChangeError', () => NProgress.done());
export default function App({ Component, pageProps }: AppProps) {
// Create a client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: false,
cacheTime: 0
}
// Create a client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
retry: false,
cacheTime: 0
}
});
}
});
export default function App({ Component, pageProps }: AppProps) {
return (
<>
<Head>

View File

@@ -1,6 +1,6 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from 'next';
import { connectToDatabase, Chat, ChatWindow } from '@/service/mongo';
import { Readable } from 'stream';
import { connectToDatabase, ChatWindow } from '@/service/mongo';
import type { ModelType } from '@/types/model';
import { getOpenAIApi, authChat } from '@/service/utils/chat';
import { openaiProxy } from '@/service/utils/tools';
@@ -9,12 +9,23 @@ import { ChatItemType } from '@/types/chat';
/* 发送提示词 */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Encoding': 'none',
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream'
res.setHeader('Connection', 'keep-alive');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Content-Type', 'text/event-stream');
const responseData: string[] = [];
const stream = new Readable({
read(size) {
const data = responseData.shift() || null;
this.push(data);
}
});
res.on('close', () => {
res.end();
stream.destroy();
});
const { chatId, windowId } = req.query as { chatId: string; windowId: string };
try {
@@ -47,14 +58,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const formatPrompts: ChatCompletionRequestMessage[] = filterPrompts.map(
(item: ChatItemType) => ({
role: map[item.obj],
content: item.value
content: item.value.replace(/(\n| )/g, '')
})
);
// 第一句话,强调代码类型
formatPrompts.unshift({
role: ChatCompletionRequestMessageRoleEnum.System,
content:
'If the content is code or code blocks, please label the code type as accurately as possible.'
'If the content is code or code blocks, please mark the code type as accurately as possible!'
});
// 获取 chatAPI
@@ -74,43 +85,75 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const reg = /{"content"(.*)"}/g;
// @ts-ignore
const match = chatResponse.data.match(reg);
if (!match) return;
let AIResponse = '';
if (match) {
match.forEach((item: string, i: number) => {
try {
const json = JSON.parse(item);
// 开头的换行忽略
if (i === 0 && json.content?.startsWith('\n')) return;
AIResponse += json.content;
const content = json.content.replace(/\n/g, '<br/>'); // 无法直接传输\n
content && res.write(`data: ${content}\n\n`);
} catch (err) {
err;
}
});
}
res.write(`data: [DONE]\n\n`);
// 循环给 stream push 内容
match.forEach((item: string, i: number) => {
try {
const json = JSON.parse(item);
// 开头的换行忽略
if (i === 0 && json.content?.startsWith('\n')) return;
AIResponse += json.content;
const content = json.content.replace(/\n/g, '<br/>'); // 无法直接传输\n
if (content) {
responseData.push(`event: responseData\ndata: ${content}\n\n`);
// res.write(`event: responseData\n`)
// res.write(`data: ${content}\n\n`)
}
} catch (err) {
err;
}
});
responseData.push(`event: done\ndata: \n\n`);
// 存入库
await ChatWindow.findByIdAndUpdate(windowId, {
$push: {
content: {
obj: 'AI',
value: AIResponse
}
},
updateTime: Date.now()
});
res.end();
(async () => {
await ChatWindow.findByIdAndUpdate(windowId, {
$push: {
content: {
obj: 'AI',
value: AIResponse
}
},
updateTime: Date.now()
});
})();
} catch (err: any) {
console.log(err?.response?.data || err);
// 删除最一条数据库记录, 也就是预发送的那一条
await ChatWindow.findByIdAndUpdate(windowId, {
$pop: { content: 1 },
updateTime: Date.now()
});
let errorText = err;
if (err.code === 'ECONNRESET') {
errorText = '服务器代理出错';
} else {
switch (err?.response?.data?.error?.code) {
case 'invalid_api_key':
errorText = 'API-KEY不合法';
break;
case 'context_length_exceeded':
errorText = '内容超长了,请重置对话';
break;
case 'rate_limit_reached':
errorText = '同时访问用户过多,请稍后再试';
break;
case null:
errorText = 'OpenAI 服务器访问超时';
break;
default:
errorText = '服务器异常';
}
}
console.error(errorText);
responseData.push(`event: serviceError\ndata: ${errorText}\n\n`);
res.end();
// 删除最一条数据库记录, 也就是预发送的那一条
(async () => {
await ChatWindow.findByIdAndUpdate(windowId, {
$pop: { content: 1 },
updateTime: Date.now()
});
})();
}
// 开启 stream 传输
stream.pipe(res);
}

View File

@@ -23,7 +23,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
// 安全校验
if (chat.loadAmount === 0 || chat.expiredTime < Date.now()) {
if (!chat || chat.loadAmount === 0 || chat.expiredTime < Date.now()) {
throw new Error('聊天框已过期');
}
@@ -82,7 +82,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
}
});
} catch (err) {
console.log(err);
jsonRes(res, {
code: 500,
error: err

View File

@@ -1,24 +0,0 @@
import type { NextApiRequest, NextApiResponse } from 'next';
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
if (req.method !== 'GET') return;
res.writeHead(200, {
Connection: 'keep-alive',
'Content-Encoding': 'none',
'Cache-Control': 'no-cache',
'Content-Type': 'text/event-stream'
});
let val = 0;
const timer = setInterval(() => {
console.log('发送消息', val);
res.write(`data: ${val++}\n\n`);
if (val > 30) {
clearInterval(timer);
res.write(`data: [DONE]\n\n`);
res.end();
}
}, 500);
}

View File

@@ -14,7 +14,6 @@ import { useToast } from '@/hooks/useToast';
import Icon from '@/components/Icon';
import { useScreen } from '@/hooks/useScreen';
import { useQuery } from '@tanstack/react-query';
import { useLoading } from '@/hooks/useLoading';
import { OpenAiModelEnum } from '@/constants/model';
import dynamic from 'next/dynamic';
import { useGlobalStore } from '@/store/global';
@@ -75,9 +74,9 @@ const Chat = () => {
scrollToBottom();
setLoading(false);
},
onError() {
onError(e: any) {
toast({
title: '初始化异常,请刷新',
title: e?.message || '初始化异常,请检查地址',
status: 'error',
isClosable: true,
duration: 5000
@@ -124,36 +123,55 @@ const Chat = () => {
return new Promise((resolve, reject) => {
const event = getChatGPTSendEvent(chatId, windowId);
event.onmessage = ({ data }) => {
if (data === '[DONE]') {
event.close();
setChatList((state) =>
state.map((item, index) => {
if (index !== state.length - 1) return item;
return {
...item,
status: 'finish'
};
})
);
resolve('');
} else if (data) {
const msg = data.replace(/<br\/>/g, '\n');
setChatList((state) =>
state.map((item, index) => {
if (index !== state.length - 1) return item;
return {
...item,
value: item.value + msg
};
})
);
}
};
event.onerror = (err) => {
console.error(err, '===');
// 30s 收不到消息就报错
let timer = setTimeout(() => {
event.close();
reject('对话出现错误');
reject('服务器超时');
}, 300000);
event.addEventListener('responseData', ({ data }) => {
/* 重置定时器 */
clearTimeout(timer);
timer = setTimeout(() => {
event.close();
reject('服务器超时');
}, 300000);
const msg = data.replace(/<br\/>/g, '\n');
setChatList((state) =>
state.map((item, index) => {
if (index !== state.length - 1) return item;
return {
...item,
value: item.value + msg
};
})
);
});
event.addEventListener('done', () => {
clearTimeout(timer);
event.close();
setChatList((state) =>
state.map((item, index) => {
if (index !== state.length - 1) return item;
return {
...item,
status: 'finish'
};
})
);
resolve('');
});
event.addEventListener('serviceError', ({ data: err }) => {
clearTimeout(timer);
event.close();
console.error(err, '===');
reject(typeof err === 'string' ? err : '对话出现不知名错误~');
});
event.onerror = (err) => {
clearTimeout(timer);
event.close();
console.error(err);
reject(typeof err === 'string' ? err : '对话出现不知名错误~');
};
});
},
@@ -300,8 +318,8 @@ const Chat = () => {
<Flex maxW={'800px'} m={'auto'} alignItems={'flex-start'}>
<Box mr={media(4, 1)}>
<Image
src={item.obj === 'Human' ? '/imgs/human.png' : '/imgs/modelAvatar.png'}
alt="/imgs/modelAvatar.png"
src={item.obj === 'Human' ? '/icon/human.png' : '/icon/logo.png'}
alt="/icon/logo.png"
width={30}
height={30}
/>
@@ -320,6 +338,16 @@ const Chat = () => {
</Box>
))}
</Box>
{/* 空内容提示 */}
{/* {
chatList.length === 0 && (
<>
<Card>
内容太长
</Card>
</>
)
} */}
<Box
m={media('20px auto', '0 auto')}
w={media('100vw', '100%')}

View File

@@ -50,10 +50,6 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
async ({ email, code, password }: RegisterType) => {
setRequesting(true);
try {
toast({
title: `密码已找回`,
status: 'success'
});
loginSuccess(
await postFindPassword({
email,
@@ -61,6 +57,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
password
})
);
toast({
title: `密码已找回`,
status: 'success'
});
} catch (error) {
typeof error === 'string' &&
toast({

View File

@@ -32,16 +32,16 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
async ({ email, password }: LoginFormType) => {
setRequesting(true);
try {
toast({
title: '登录成功',
status: 'success'
});
loginSuccess(
await postLogin({
email,
password
})
);
toast({
title: '登录成功',
status: 'success'
});
} catch (error) {
typeof error === 'string' &&
toast({

View File

@@ -50,10 +50,6 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
async ({ email, password, code }: RegisterType) => {
setRequesting(true);
try {
toast({
title: `注册成功`,
status: 'success'
});
loginSuccess(
await postRegister({
email,
@@ -61,6 +57,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
password
})
);
toast({
title: `注册成功`,
status: 'success'
});
} catch (error) {
typeof error === 'string' &&
toast({

View File

@@ -34,7 +34,7 @@ const ModelEditForm = ({ model }: { model?: ModelType }) => {
status: 'success'
});
} catch (err) {
console.log(err);
console.error(err);
toast({
title: err as string,
status: 'success'

View File

@@ -29,7 +29,7 @@ const Training = ({ model }: { model: ModelType }) => {
const res = await getModelTrainings(id);
setRecords(res);
} catch (error) {
console.log(error);
console.error(error);
}
}, []);

View File

@@ -41,10 +41,8 @@ const ModelDetail = () => {
const res = await getModelById(modelId as string);
res.security.expiredTime /= 60 * 60 * 1000;
setModel(res);
console.log(res);
} catch (err) {
console.log(err);
console.error(err);
}
setLoading(false);
}, [modelId, setLoading]);
@@ -65,7 +63,7 @@ const ModelDetail = () => {
});
router.replace('/model/list');
} catch (err) {
console.log(err);
console.error(err);
}
setLoading(false);
}, [setLoading, model, router, toast]);
@@ -79,7 +77,7 @@ const ModelDetail = () => {
router.push(`/chat?chatId=${chatId}`);
} catch (err) {
console.log(err);
console.error(err);
}
setLoading(false);
}, [setLoading, model, router]);
@@ -107,7 +105,7 @@ const ModelDetail = () => {
title: typeof err === 'string' ? err : '文件格式错误',
status: 'error'
});
console.log(err);
console.error(err);
}
setLoading(false);
},
@@ -123,7 +121,7 @@ const ModelDetail = () => {
await putModelTrainingStatus(model._id);
loadModel();
} catch (error) {
console.log(error);
console.error(error);
}
setLoading(false);
}, [setLoading, loadModel, model]);

View File

@@ -44,7 +44,7 @@ const ModelList = () => {
shallow: true
});
} catch (err) {
console.log(err);
console.error(err);
}
setIsLoading(false);
},

View File

@@ -24,8 +24,8 @@ export const jsonRes = (
typeof error === 'string'
? error
: openaiError[error?.response?.data?.message] || error?.message || '请求错误';
console.log(msg);
console.error(error);
console.error(msg);
}
res.json({

View File

@@ -34,7 +34,7 @@ export const sendCode = (email: string, code: string, type: `${EmailTypeEnum}`)
};
mailTransport.sendMail(options, function (err, msg) {
if (err) {
console.log(err);
console.error(err);
reject('邮箱异常');
} else {
resolve('');
@@ -53,7 +53,7 @@ export const sendTrainSucceed = (email: string, modelName: string) => {
};
mailTransport.sendMail(options, function (err, msg) {
if (err) {
console.log(err);
console.error(err);
reject('邮箱异常');
} else {
resolve('');

View File

@@ -21,7 +21,7 @@ export const useCopyData = () => {
duration: 1000
});
} catch (error) {
console.log(error);
console.error(error);
toast({
title: '复制失败',
status: 'error'