feat: 注册限流配置

feat: 页面加载动画
feat: md样式优化
feat: 移动端全屏覆盖
This commit is contained in:
archer 2023-03-04 13:30:20 +08:00
parent 0ecf576e4e
commit 2cc32d1806
28 changed files with 515 additions and 252 deletions

View File

@ -1,6 +1,6 @@
AXIOS_PROXY_HOST=127.0.0.1 AXIOS_PROXY_HOST=127.0.0.1
AXIOS_PROXY_PORT=33210 AXIOS_PROXY_PORT=33210
MONGODB_UR= MONGODB_URI=
MY_MAIL= MY_MAIL=
MAILE_CODE= MAILE_CODE=
TOKEN_KEY= TOKEN_KEY=

1
.gitignore vendored
View File

@ -36,3 +36,4 @@ yarn-error.log*
next-env.d.ts next-env.d.ts
public/trainData/ public/trainData/
.vscode/ .vscode/
platform.json

View File

@ -55,5 +55,12 @@ USER nextjs
EXPOSE 3000 EXPOSE 3000
ENV PORT 3000 ENV PORT 3000
ENV MAX_USER ''
ENV AXIOS_PROXY_HOST ''
ENV AXIOS_PROXY_PORT ''
ENV MONGODB_URI ''
ENV MY_MAIL ''
ENV MAILE_CODE ''
ENV TOKEN_KEY ''
CMD ["node", "server.js"] CMD ["node", "server.js"]

View File

@ -6,7 +6,7 @@
``` ```
AXIOS_PROXY_HOST=axios代理地址目前 openai 接口都需要走代理,本机的话就填 127.0.0.1 AXIOS_PROXY_HOST=axios代理地址目前 openai 接口都需要走代理,本机的话就填 127.0.0.1
AXIOS_PROXY_PORT=代理端口 AXIOS_PROXY_PORT=代理端口
MONGODB_UR=mongo数据库地址 MONGODB_URI=mongo数据库地址
MY_MAIL=发送验证码邮箱 MY_MAIL=发送验证码邮箱
MAILE_CODE=邮箱秘钥 MAILE_CODE=邮箱秘钥
TOKEN_KEY=随便填一个用于生成和校验token TOKEN_KEY=随便填一个用于生成和校验token
@ -27,7 +27,7 @@ docker pull imageName
docker stop doc-gpt || true docker stop doc-gpt || true
docker rm 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_UR= imageName 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
``` ```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.

View File

@ -19,6 +19,7 @@
"@next/font": "13.1.6", "@next/font": "13.1.6",
"@reduxjs/toolkit": "^1.9.3", "@reduxjs/toolkit": "^1.9.3",
"@tanstack/react-query": "^4.24.10", "@tanstack/react-query": "^4.24.10",
"@types/nprogress": "^0.2.0",
"axios": "^1.3.3", "axios": "^1.3.3",
"crypto": "^1.0.1", "crypto": "^1.0.1",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
@ -32,6 +33,7 @@
"mongoose": "^6.10.0", "mongoose": "^6.10.0",
"next": "13.1.6", "next": "13.1.6",
"nodemailer": "^6.9.1", "nodemailer": "^6.9.1",
"nprogress": "^0.2.0",
"openai": "^3.2.1", "openai": "^3.2.1",
"react": "18.2.0", "react": "18.2.0",
"react-dom": "18.2.0", "react-dom": "18.2.0",

37
pnpm-lock.yaml generated
View File

@ -13,6 +13,7 @@ specifiers:
'@types/jsonwebtoken': ^9.0.1 '@types/jsonwebtoken': ^9.0.1
'@types/node': 18.14.0 '@types/node': 18.14.0
'@types/nodemailer': ^6.4.7 '@types/nodemailer': ^6.4.7
'@types/nprogress': ^0.2.0
'@types/react': 18.0.28 '@types/react': 18.0.28
'@types/react-dom': 18.0.11 '@types/react-dom': 18.0.11
'@types/react-syntax-highlighter': ^15.5.6 '@types/react-syntax-highlighter': ^15.5.6
@ -33,6 +34,7 @@ specifiers:
mongoose: ^6.10.0 mongoose: ^6.10.0
next: 13.1.6 next: 13.1.6
nodemailer: ^6.9.1 nodemailer: ^6.9.1
nprogress: ^0.2.0
openai: ^3.2.1 openai: ^3.2.1
prettier: ^2.8.4 prettier: ^2.8.4
react: 18.2.0 react: 18.2.0
@ -57,6 +59,7 @@ dependencies:
'@next/font': registry.npmmirror.com/@next/font/13.1.6 '@next/font': registry.npmmirror.com/@next/font/13.1.6
'@reduxjs/toolkit': registry.npmmirror.com/@reduxjs/toolkit/1.9.3_react@18.2.0 '@reduxjs/toolkit': registry.npmmirror.com/@reduxjs/toolkit/1.9.3_react@18.2.0
'@tanstack/react-query': registry.npmmirror.com/@tanstack/react-query/4.24.10_biqbaboplfbrettd7655fr4n2y '@tanstack/react-query': registry.npmmirror.com/@tanstack/react-query/4.24.10_biqbaboplfbrettd7655fr4n2y
'@types/nprogress': registry.npmmirror.com/@types/nprogress/0.2.0
axios: registry.npmmirror.com/axios/1.3.3 axios: registry.npmmirror.com/axios/1.3.3
crypto: registry.npmmirror.com/crypto/1.0.1 crypto: registry.npmmirror.com/crypto/1.0.1
dayjs: registry.npmmirror.com/dayjs/1.11.7 dayjs: registry.npmmirror.com/dayjs/1.11.7
@ -70,6 +73,7 @@ dependencies:
mongoose: registry.npmmirror.com/mongoose/6.10.0 mongoose: registry.npmmirror.com/mongoose/6.10.0
next: registry.npmmirror.com/next/13.1.6_wiv434v7erz4aedd5whhdwmpv4 next: registry.npmmirror.com/next/13.1.6_wiv434v7erz4aedd5whhdwmpv4
nodemailer: registry.npmmirror.com/nodemailer/6.9.1 nodemailer: registry.npmmirror.com/nodemailer/6.9.1
nprogress: registry.npmmirror.com/nprogress/0.2.0
openai: registry.npmmirror.com/openai/3.2.1 openai: registry.npmmirror.com/openai/3.2.1
react: registry.npmmirror.com/react/18.2.0 react: registry.npmmirror.com/react/18.2.0
react-dom: registry.npmmirror.com/react-dom/18.2.0_react@18.2.0 react-dom: registry.npmmirror.com/react-dom/18.2.0_react@18.2.0
@ -1097,6 +1101,15 @@ packages:
regenerator-runtime: registry.npmmirror.com/regenerator-runtime/0.13.11 regenerator-runtime: registry.npmmirror.com/regenerator-runtime/0.13.11
dev: false dev: false
registry.npmmirror.com/@babel/runtime/7.21.0:
resolution: {integrity: sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/runtime/-/runtime-7.21.0.tgz}
name: '@babel/runtime'
version: 7.21.0
engines: {node: '>=6.9.0'}
dependencies:
regenerator-runtime: registry.npmmirror.com/regenerator-runtime/0.13.11
dev: false
registry.npmmirror.com/@babel/types/7.21.0: registry.npmmirror.com/@babel/types/7.21.0:
resolution: {integrity: sha512-uR7NWq2VNFnDi7EYqiRz2Jv/VQIu38tu64Zy8TX2nQFQ6etJ9V/Rr2msW8BS132mum2rL645qpDrLtAJtVpuow==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/types/-/types-7.21.0.tgz} resolution: {integrity: sha512-uR7NWq2VNFnDi7EYqiRz2Jv/VQIu38tu64Zy8TX2nQFQ6etJ9V/Rr2msW8BS132mum2rL645qpDrLtAJtVpuow==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/types/-/types-7.21.0.tgz}
name: '@babel/types' name: '@babel/types'
@ -2433,7 +2446,7 @@ packages:
version: 11.10.6 version: 11.10.6
dependencies: dependencies:
'@babel/helper-module-imports': registry.npmmirror.com/@babel/helper-module-imports/7.18.6 '@babel/helper-module-imports': registry.npmmirror.com/@babel/helper-module-imports/7.18.6
'@babel/runtime': registry.npmmirror.com/@babel/runtime/7.20.13 '@babel/runtime': registry.npmmirror.com/@babel/runtime/7.21.0
'@emotion/hash': registry.npmmirror.com/@emotion/hash/0.9.0 '@emotion/hash': registry.npmmirror.com/@emotion/hash/0.9.0
'@emotion/memoize': registry.npmmirror.com/@emotion/memoize/0.8.0 '@emotion/memoize': registry.npmmirror.com/@emotion/memoize/0.8.0
'@emotion/serialize': registry.npmmirror.com/@emotion/serialize/1.1.1 '@emotion/serialize': registry.npmmirror.com/@emotion/serialize/1.1.1
@ -3048,6 +3061,12 @@ packages:
'@types/node': registry.npmmirror.com/@types/node/18.14.0 '@types/node': registry.npmmirror.com/@types/node/18.14.0
dev: true dev: true
registry.npmmirror.com/@types/nprogress/0.2.0:
resolution: {integrity: sha512-1cYJrqq9GezNFPsWTZpFut/d4CjpZqA0vhqDUPFWYKF1oIyBz5qnoYMzR+0C/T96t3ebLAC1SSnwrVOm5/j74A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/nprogress/-/nprogress-0.2.0.tgz}
name: '@types/nprogress'
version: 0.2.0
dev: false
registry.npmmirror.com/@types/parse-json/4.0.0: registry.npmmirror.com/@types/parse-json/4.0.0:
resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.0.tgz} resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.0.tgz}
name: '@types/parse-json' name: '@types/parse-json'
@ -3469,7 +3488,7 @@ packages:
version: 3.1.0 version: 3.1.0
engines: {node: '>=10', npm: '>=6'} engines: {node: '>=10', npm: '>=6'}
dependencies: dependencies:
'@babel/runtime': registry.npmmirror.com/@babel/runtime/7.20.13 '@babel/runtime': registry.npmmirror.com/@babel/runtime/7.21.0
cosmiconfig: registry.npmmirror.com/cosmiconfig/7.1.0 cosmiconfig: registry.npmmirror.com/cosmiconfig/7.1.0
resolve: registry.npmmirror.com/resolve/1.22.1 resolve: registry.npmmirror.com/resolve/1.22.1
dev: false dev: false
@ -4329,7 +4348,7 @@ packages:
peerDependencies: peerDependencies:
eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
dependencies: dependencies:
'@babel/runtime': registry.npmmirror.com/@babel/runtime/7.20.13 '@babel/runtime': registry.npmmirror.com/@babel/runtime/7.21.0
aria-query: registry.npmmirror.com/aria-query/5.1.3 aria-query: registry.npmmirror.com/aria-query/5.1.3
array-includes: registry.npmmirror.com/array-includes/3.1.6 array-includes: registry.npmmirror.com/array-includes/3.1.6
array.prototype.flatmap: registry.npmmirror.com/array.prototype.flatmap/1.3.1 array.prototype.flatmap: registry.npmmirror.com/array.prototype.flatmap/1.3.1
@ -6481,6 +6500,12 @@ packages:
path-key: registry.npmmirror.com/path-key/4.0.0 path-key: registry.npmmirror.com/path-key/4.0.0
dev: true dev: true
registry.npmmirror.com/nprogress/0.2.0:
resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/nprogress/-/nprogress-0.2.0.tgz}
name: nprogress
version: 0.2.0
dev: false
registry.npmmirror.com/object-assign/4.1.1: registry.npmmirror.com/object-assign/4.1.1:
resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz} resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz}
name: object-assign name: object-assign
@ -6889,7 +6914,7 @@ packages:
peerDependencies: peerDependencies:
react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 react: ^15.3.0 || ^16.0.0 || ^17.0.0 || ^18.0.0
dependencies: dependencies:
'@babel/runtime': registry.npmmirror.com/@babel/runtime/7.20.13 '@babel/runtime': registry.npmmirror.com/@babel/runtime/7.21.0
react: registry.npmmirror.com/react/18.2.0 react: registry.npmmirror.com/react/18.2.0
dev: false dev: false
@ -6924,7 +6949,7 @@ packages:
'@types/react': '@types/react':
optional: true optional: true
dependencies: dependencies:
'@babel/runtime': registry.npmmirror.com/@babel/runtime/7.20.13 '@babel/runtime': registry.npmmirror.com/@babel/runtime/7.21.0
'@types/react': registry.npmmirror.com/@types/react/18.0.28 '@types/react': registry.npmmirror.com/@types/react/18.0.28
focus-lock: registry.npmmirror.com/focus-lock/0.11.6 focus-lock: registry.npmmirror.com/focus-lock/0.11.6
prop-types: registry.npmmirror.com/prop-types/15.8.1 prop-types: registry.npmmirror.com/prop-types/15.8.1
@ -7110,7 +7135,7 @@ packages:
name: redux name: redux
version: 4.2.1 version: 4.2.1
dependencies: dependencies:
'@babel/runtime': registry.npmmirror.com/@babel/runtime/7.20.13 '@babel/runtime': registry.npmmirror.com/@babel/runtime/7.21.0
dev: false dev: false
registry.npmmirror.com/refractor/3.6.0: registry.npmmirror.com/refractor/3.6.0:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 323 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 205 KiB

After

Width:  |  Height:  |  Size: 117 KiB

View File

@ -7,6 +7,7 @@ import { useGlobalStore } from '@/store/global';
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
const unAuthPage: { [key: string]: boolean } = { const unAuthPage: { [key: string]: boolean } = {
'/': true,
'/login': true, '/login': true,
'/chat': true '/chat': true
}; };
@ -48,7 +49,7 @@ const Auth = ({ children }: { children: JSX.Element }) => {
} }
); );
return userInfo || unAuthPage[router.pathname] === true ? <>{children}</> : null; return userInfo || unAuthPage[router.pathname] === true ? children : null;
}; };
export default Auth; export default Auth;

View File

@ -51,10 +51,10 @@ const Layout = ({ children }: { children: JSX.Element }) => {
return ( return (
<> <>
{!unShowLayoutRoute[router.pathname] ? ( {!unShowLayoutRoute[router.pathname] ? (
<Box minHeight={'100vh'} backgroundColor={'gray.100'}> <Box data-test="ss" h={'100%'} backgroundColor={'gray.100'} overflow={'auto'}>
{isPc ? ( {isPc ? (
<> <>
<Box h={'100vh'} position={'fixed'} left={0} top={0} w={'80px'}> <Box h={'100%'} position={'fixed'} left={0} top={0} w={'80px'}>
<Navbar navbarList={navbarList} /> <Navbar navbarList={navbarList} />
</Box> </Box>
<Box ml={'80px'} p={7}> <Box ml={'80px'} p={7}>

View File

@ -3,7 +3,6 @@ import { Box, Flex } from '@chakra-ui/react';
import Image from 'next/image'; import Image from 'next/image';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import Icon from '../Icon'; import Icon from '../Icon';
import styles from './style.module.scss';
export enum NavbarTypeEnum { export enum NavbarTypeEnum {
normal = 'normal', normal = 'normal',

View File

@ -1,47 +1,5 @@
import React from 'react'; import React from 'react';
export const codeLight: { [key: string]: React.CSSProperties } = { 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': { 'code[class*=language-] ::selection': {
textShadow: 'none', textShadow: 'none',
background: '#264f78' background: '#264f78'

View File

@ -27,96 +27,343 @@
opacity: 1; opacity: 1;
} }
} }
.markdown {
/* 标题样式 */
h1 {
font-size: 1.8rem;
}
h2 { .markdown > *:first-child {
font-size: 1.6rem; margin-top: 0 !important;
} }
.markdown > *:last-child {
h3 { margin-bottom: 0 !important;
font-size: 1.4rem;
} }
.markdown a.absent {
h4 { color: #cc0000;
font-size: 1.2rem;
} }
.markdown a.anchor {
h5 { bottom: 0;
font-size: 1rem; cursor: pointer;
display: block;
left: 0;
margin-left: -30px;
padding-left: 30px;
position: absolute;
top: 0;
} }
.markdown h1,
h6 { .markdown h2,
font-size: 0.83rem; .markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
cursor: text;
font-weight: bold;
margin: 20px 0 10px;
padding: 0;
position: relative;
} }
.markdown h1 .mini-icon-link,
/* 列表样式 */ .markdown h2 .mini-icon-link,
ol, .markdown h3 .mini-icon-link,
ul { .markdown h4 .mini-icon-link,
padding-left: 1.5rem; .markdown h5 .mini-icon-link,
margin-left: 1rem; .markdown h6 .mini-icon-link {
color: #000000;
display: none;
} }
ul { .markdown h1:hover a.anchor,
list-style: inside; .markdown h2:hover a.anchor,
} .markdown h3:hover a.anchor,
ol { .markdown h4:hover a.anchor,
list-style: decimal; .markdown h5:hover a.anchor,
} .markdown h6:hover a.anchor {
line-height: 1;
/* 链接样式 */ margin-left: -22px;
a { padding-left: 0;
color: #0077cc;
text-decoration: none; text-decoration: none;
border-bottom: 1px solid #0077cc; top: 15%;
} }
.markdown h1:hover a.anchor .mini-icon-link,
a:hover { .markdown h2:hover a.anchor .mini-icon-link,
color: #005580; .markdown h3:hover a.anchor .mini-icon-link,
border-bottom-color: #005580; .markdown h4:hover a.anchor .mini-icon-link,
.markdown h5:hover a.anchor .mini-icon-link,
.markdown h6:hover a.anchor .mini-icon-link {
display: inline-block;
} }
.markdown h1 tt,
/* 图片样式 */ .markdown h1 code,
img { .markdown h2 tt,
max-width: 100%; .markdown h2 code,
max-height: 200px; .markdown h3 tt,
margin: auto; .markdown h3 code,
.markdown h4 tt,
.markdown h4 code,
.markdown h5 tt,
.markdown h5 code,
.markdown h6 tt,
.markdown h6 code {
font-size: inherit;
} }
.markdown h1 {
/* 强调样式 */ color: #000000;
em, font-size: 28px;
i { }
.markdown h2 {
border-bottom: 1px solid #cccccc;
color: #000000;
font-size: 24px;
}
.markdown h3 {
font-size: 18px;
}
.markdown h4 {
font-size: 16px;
}
.markdown h5 {
font-size: 14px;
}
.markdown h6 {
color: #777777;
font-size: 14px;
}
.markdown p,
.markdown blockquote,
.markdown ul,
.markdown ol,
.markdown dl,
.markdown table {
margin: 15px 0;
}
.markdown hr {
background: url('https://a248.e.akamai.net/assets.github.com/assets/primer/markdown/dirty-shade-350cca8f57223ebd53603021b2e670f4f319f1b7.png')
repeat-x scroll 0 0 transparent;
border: 0 none;
color: #cccccc;
height: 4px;
padding: 0;
}
.markdown > h2:first-child,
.markdown > h1:first-child,
.markdown > h1:first-child + h2,
.markdown > h3:first-child,
.markdown > h4:first-child,
.markdown > h5:first-child,
.markdown > h6:first-child {
margin-top: 0;
padding-top: 0;
}
.markdown a:first-child h1,
.markdown a:first-child h2,
.markdown a:first-child h3,
.markdown a:first-child h4,
.markdown a:first-child h5,
.markdown a:first-child h6 {
margin-top: 0;
padding-top: 0;
}
.markdown h1 + p,
.markdown h2 + p,
.markdown h3 + p,
.markdown h4 + p,
.markdown h5 + p,
.markdown h6 + p {
margin-top: 0;
}
.markdown li p.first {
display: inline-block;
}
.markdown ul,
.markdown ol {
padding-left: 30px;
}
.markdown ul.no-list,
.markdown ol.no-list {
list-style-type: none;
padding: 0;
}
.markdown ul li > *:first-child,
.markdown ol li > *:first-child {
margin-top: 0;
}
.markdown ul ul,
.markdown ul ol,
.markdown ol ol,
.markdown ol ul {
margin-bottom: 0;
}
.markdown dl {
padding: 0;
}
.markdown dl dt {
font-size: 14px;
font-style: italic; font-style: italic;
font-weight: bold;
margin: 15px 0 5px;
padding: 0;
} }
.markdown dl dt:first-child {
strong, padding: 0;
b { }
.markdown dl dt > *:first-child {
margin-top: 0;
}
.markdown dl dt > *:last-child {
margin-bottom: 0;
}
.markdown dl dd {
margin: 0 0 15px;
padding: 0 15px;
}
.markdown dl dd > *:first-child {
margin-top: 0;
}
.markdown dl dd > *:last-child {
margin-bottom: 0;
}
.markdown blockquote {
border-left: 4px solid #dddddd;
color: #777777;
padding: 0 15px;
}
.markdown blockquote > *:first-child {
margin-top: 0;
}
.markdown blockquote > *:last-child {
margin-bottom: 0;
}
.markdown table th {
font-weight: bold; font-weight: bold;
} }
.markdown table th,
/* 代码样式 */ .markdown table td {
code { border: 1px solid #cccccc;
border-radius: 3px; padding: 6px 13px;
width: 100%;
} }
.markdown table tr {
background-color: #ffffff;
border-top: 1px solid #cccccc;
}
.markdown table tr:nth-child(2n) {
background-color: #f8f8f8;
}
.markdown img {
max-width: 100%;
}
.markdown span.frame {
display: block;
overflow: hidden;
}
.markdown span.frame > span {
border: 1px solid #dddddd;
display: block;
float: left;
margin: 13px 0 0;
overflow: hidden;
padding: 7px;
width: auto;
}
.markdown span.frame span img {
display: block;
float: left;
}
.markdown span.frame span span {
clear: both;
color: #333333;
display: block;
padding: 5px 0 0;
}
.markdown span.align-center {
clear: both;
display: block;
overflow: hidden;
}
.markdown span.align-center > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: center;
}
.markdown span.align-center span img {
margin: 0 auto;
text-align: center;
}
.markdown span.align-right {
clear: both;
display: block;
overflow: hidden;
}
.markdown span.align-right > span {
display: block;
margin: 13px 0 0;
overflow: hidden;
text-align: right;
}
.markdown span.align-right span img {
margin: 0;
text-align: right;
}
.markdown span.float-left {
display: block;
float: left;
margin-right: 13px;
overflow: hidden;
}
.markdown span.float-left span {
margin: 13px 0 0;
}
.markdown span.float-right {
display: block;
float: right;
margin-left: 13px;
overflow: hidden;
}
.markdown span.float-right > span {
display: block;
margin: 13px auto 0;
overflow: hidden;
text-align: right;
}
.markdown code,
.markdown tt {
background-color: #f8f8f8;
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;
margin: 0;
padding: 0;
white-space: pre;
}
.markdown .highlight pre,
.markdown pre {
background-color: #f8f8f8;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
}
.markdown pre code,
.markdown pre tt {
background-color: transparent;
border: medium none;
}
.markdown {
font-size: 14px;
line-height: 1.6;
letter-spacing: 0.5px;
text-align: justify;
pre { pre {
padding: 10px 15px; display: block;
width: 100%; width: 100%;
padding: 15px;
background-color: #222 !important; background-color: #222 !important;
overflow-x: auto; overflow-x: auto;
} }
pre code { pre code {
display: block;
border: none;
background-color: #222; background-color: #222;
color: #fff; color: #fff;
} width: 100%;
p {
line-height: 1.7;
} }
} }

View File

@ -1,4 +1,4 @@
import React, { useMemo, memo } from 'react'; import React, { memo } from 'react';
import ReactMarkdown from 'react-markdown'; import ReactMarkdown from 'react-markdown';
import remarkGfm from 'remark-gfm'; import remarkGfm from 'remark-gfm';
import styles from './index.module.scss'; import styles from './index.module.scss';
@ -24,10 +24,16 @@ const Markdown = ({ source, isChatting }: { source: string; isChatting: boolean
code({ node, inline, className, children, ...props }) { code({ node, inline, className, children, ...props }) {
const match = /language-(\w+)/.exec(className || ''); const match = /language-(\w+)/.exec(className || '');
const code = String(children).replace(/\n$/, ''); const code = String(children).replace(/\n$/, '');
return !inline ? (
return (
<Box my={3} borderRadius={'md'} overflow={'hidden'}> <Box my={3} borderRadius={'md'} overflow={'hidden'}>
<Flex py={2} px={5} backgroundColor={'#323641'} color={'#fff'} fontSize={'sm'}> <Flex
py={2}
px={5}
backgroundColor={'#323641'}
color={'#fff'}
fontSize={'sm'}
userSelect={'none'}
>
<Box flex={1}>{match?.[1]}</Box> <Box flex={1}>{match?.[1]}</Box>
<Flex cursor={'pointer'} onClick={() => copyData(code)} alignItems={'center'}> <Flex cursor={'pointer'} onClick={() => copyData(code)} alignItems={'center'}>
<Icon name={'icon-fuzhi'} width={15} height={15} color={'#fff'}></Icon> <Icon name={'icon-fuzhi'} width={15} height={15} color={'#fff'}></Icon>
@ -36,13 +42,17 @@ const Markdown = ({ source, isChatting }: { source: string; isChatting: boolean
</Flex> </Flex>
<SyntaxHighlighter <SyntaxHighlighter
style={codeLight as any} style={codeLight as any}
showLineNumbers language={match?.[1] || 'bash'}
language={match?.[1]} PreTag="pre"
{...props} {...props}
> >
{code} {code}
</SyntaxHighlighter> </SyntaxHighlighter>
</Box> </Box>
) : (
<code className={className} {...props}>
{children}
</code>
); );
} }
}} }}

View File

@ -58,7 +58,11 @@ export const theme = extendTheme({
global: { global: {
'html, body': { 'html, body': {
color: 'blackAlpha.800', color: 'blackAlpha.800',
fontSize: '14px' 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'
} }
} }
}, },

View File

@ -1,11 +1,20 @@
import type { AppProps, NextWebVitalsMetric } from 'next/app'; import type { AppProps, NextWebVitalsMetric } from 'next/app';
import Script from 'next/script';
import Head from 'next/head'; import Head from 'next/head';
import { ChakraProvider } from '@chakra-ui/react'; import { ChakraProvider } from '@chakra-ui/react';
import Layout from '@/components/Layout'; import Layout from '@/components/Layout';
import { theme } from '@/constants/theme'; import { theme } from '@/constants/theme';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import NProgress from 'nprogress'; //nprogress module
import Router from 'next/router';
import 'nprogress/nprogress.css';
import '../styles/reset.scss'; import '../styles/reset.scss';
//Binding events.
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) { export default function App({ Component, pageProps }: AppProps) {
// Create a client // Create a client
const queryClient = new QueryClient({ const queryClient = new QueryClient({
@ -28,7 +37,6 @@ export default function App({ Component, pageProps }: AppProps) {
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;"
/> />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<script src="/iconfont.js" async></script>
</Head> </Head>
<QueryClientProvider client={queryClient}> <QueryClientProvider client={queryClient}>
<ChakraProvider theme={theme}> <ChakraProvider theme={theme}>
@ -37,6 +45,7 @@ export default function App({ Component, pageProps }: AppProps) {
</Layout> </Layout>
</ChakraProvider> </ChakraProvider>
</QueryClientProvider> </QueryClientProvider>
<Script src="/iconfont.js"></Script>
</> </>
); );
} }

View File

@ -2,13 +2,13 @@
import type { NextApiRequest, NextApiResponse } from 'next'; import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@/service/response'; import { jsonRes } from '@/service/response';
import { AuthCode } from '@/service/models/authCode'; import { AuthCode } from '@/service/models/authCode';
import { connectToDatabase } from '@/service/mongo'; import { connectToDatabase, User } from '@/service/mongo';
import { sendCode } from '@/service/utils/sendEmail'; import { sendCode } from '@/service/utils/sendEmail';
import { EmailTypeEnum } from '@/constants/common'; import { EmailTypeEnum } from '@/constants/common';
export default async function handler(req: NextApiRequest, res: NextApiResponse) { export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try { try {
const { email, type } = req.query; const { email, type } = req.query as { email: string; type: `${EmailTypeEnum}` };
if (!email || !type) { if (!email || !type) {
throw new Error('缺少参数'); throw new Error('缺少参数');
@ -16,6 +16,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await connectToDatabase(); await connectToDatabase();
// 注册人数限流
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('当前注册用户已满,请等待名额~');
}
}
let code = ''; let code = '';
for (let i = 0; i < 6; i++) { for (let i = 0; i < 6; i++) {
code += Math.floor(Math.random() * 10); code += Math.floor(Math.random() * 10);

View File

@ -18,10 +18,12 @@ import { useQuery } from '@tanstack/react-query';
import { useLoading } from '@/hooks/useLoading'; import { useLoading } from '@/hooks/useLoading';
import { OpenAiModelEnum } from '@/constants/model'; import { OpenAiModelEnum } from '@/constants/model';
const textareaMinH = '22px';
const Chat = () => { const Chat = () => {
const { toast } = useToast(); const { toast } = useToast();
const router = useRouter(); const router = useRouter();
const { media } = useScreen(); const { isPc, media } = useScreen();
const { chatId, windowId } = router.query as { chatId: string; windowId?: string }; const { chatId, windowId } = router.query as { chatId: string; windowId?: string };
const ChatBox = useRef<HTMLDivElement>(null); const ChatBox = useRef<HTMLDivElement>(null);
const TextareaDom = useRef<HTMLTextAreaElement>(null); const TextareaDom = useRef<HTMLTextAreaElement>(null);
@ -32,7 +34,7 @@ const Chat = () => {
const isChatting = useMemo(() => chatList[chatList.length - 1]?.status === 'loading', [chatList]); const isChatting = useMemo(() => chatList[chatList.length - 1]?.status === 'loading', [chatList]);
const lastWordHuman = useMemo(() => chatList[chatList.length - 1]?.obj === 'Human', [chatList]); const lastWordHuman = useMemo(() => chatList[chatList.length - 1]?.obj === 'Human', [chatList]);
const { Loading } = useLoading(); const { Loading, setIsLoading } = useLoading({ defaultLoading: true });
// 滚动到底部 // 滚动到底部
const scrollToBottom = useCallback(() => { const scrollToBottom = useCallback(() => {
@ -47,7 +49,10 @@ const Chat = () => {
}, []); }, []);
// 初始化聊天框 // 初始化聊天框
useQuery([chatId, windowId], () => (chatId ? getInitChatSiteInfo(chatId, windowId) : null), { const { isInitialLoading } = useQuery(
[chatId, windowId],
() => (chatId ? getInitChatSiteInfo(chatId, windowId) : null),
{
cacheTime: 5 * 60 * 1000, cacheTime: 5 * 60 * 1000,
onSuccess(res) { onSuccess(res) {
if (!res) return; if (!res) return;
@ -61,14 +66,19 @@ const Chat = () => {
})) }))
); );
scrollToBottom(); scrollToBottom();
setIsLoading(false);
}, },
onError() { onError() {
toast({ toast({
title: '初始化异常', title: '初始化异常,请刷新',
status: 'error' status: 'error',
isClosable: true,
duration: 5000
}); });
setIsLoading(false);
} }
}); }
);
// gpt3 方法 // gpt3 方法
const gpt3ChatPrompt = useCallback( const gpt3ChatPrompt = useCallback(
@ -179,8 +189,9 @@ const Chat = () => {
setTimeout(() => { setTimeout(() => {
scrollToBottom(); scrollToBottom();
/* 回到最小高度 */
if (TextareaDom.current) { if (TextareaDom.current) {
TextareaDom.current.style.height = 22 + 'px'; TextareaDom.current.style.height = textareaMinH;
} }
}, 100); }, 100);
@ -242,7 +253,7 @@ const Chat = () => {
}, [chatList, windowId]); }, [chatList, windowId]);
return ( return (
<Flex h={'100vh'} flexDirection={'column'} overflowY={'hidden'}> <Flex height={'100%'} flexDirection={'column'}>
{/* 头部 */} {/* 头部 */}
<Flex <Flex
px={4} px={4}
@ -258,7 +269,6 @@ const Chat = () => {
<Icon name={'icon-zhongzhi'} width={20} height={20} color={'#718096'}></Icon> <Icon name={'icon-zhongzhi'} width={20} height={20} color={'#718096'}></Icon>
</Box> </Box>
{/* 滚动到底部按键 */} {/* 滚动到底部按键 */}
{/* 滚动到底部 */}
{ChatBox.current && ChatBox.current.scrollHeight > 2 * ChatBox.current.clientHeight && ( {ChatBox.current && ChatBox.current.scrollHeight > 2 * ChatBox.current.clientHeight && (
<Box ml={10} cursor={'pointer'} onClick={scrollToBottom}> <Box ml={10} cursor={'pointer'} onClick={scrollToBottom}>
<Icon <Icon
@ -302,8 +312,9 @@ const Chat = () => {
<Box <Box
m={media('20px auto', '0 auto')} m={media('20px auto', '0 auto')}
w={media('100vw', '100%')} w={media('100vw', '100%')}
maxW={'800px'} maxW={media('800px', '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)')}
> >
{lastWordHuman ? ( {lastWordHuman ? (
<Box textAlign={'center'}> <Box textAlign={'center'}>
@ -349,12 +360,12 @@ const Chat = () => {
onChange={(e) => { onChange={(e) => {
const textarea = e.target; const textarea = e.target;
setInputVal(textarea.value); setInputVal(textarea.value);
textarea.style.height = textareaMinH;
textarea.style.height = textarea.value.split('\n').length * 22 + 'px'; textarea.style.height = `${textarea.scrollHeight}px`;
}} }}
onKeyDown={(e) => { onKeyDown={(e) => {
// 触发快捷发送 // 触发快捷发送
if (e.keyCode === 13 && !e.shiftKey) { if (isPc && e.keyCode === 13 && !e.shiftKey) {
sendPrompt(); sendPrompt();
e.preventDefault(); e.preventDefault();
} }
@ -382,7 +393,7 @@ const Chat = () => {
</Box> </Box>
)} )}
</Box> </Box>
<Loading loading={!chatSiteData} /> <Loading />
</Flex> </Flex>
); );
}; };

View File

@ -1,19 +1,12 @@
import React, { useState, Dispatch, useCallback } from 'react'; import React, { useState, Dispatch, useCallback } from 'react';
import { import { FormControl, Box, Input, Button, FormErrorMessage, Flex } from '@chakra-ui/react';
FormControl,
Box,
Input,
Button,
FormErrorMessage,
useToast,
Flex
} from '@chakra-ui/react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { PageTypeEnum } from '../../../constants/user'; import { PageTypeEnum } from '../../../constants/user';
import { postFindPassword } from '@/api/user'; import { postFindPassword } from '@/api/user';
import { useSendCode } from '@/hooks/useSendCode'; import { useSendCode } from '@/hooks/useSendCode';
import type { ResLogin } from '@/api/response/user'; import type { ResLogin } from '@/api/response/user';
import { useScreen } from '@/hooks/useScreen'; import { useScreen } from '@/hooks/useScreen';
import { useToast } from '@/hooks/useToast';
interface Props { interface Props {
setPageType: Dispatch<`${PageTypeEnum}`>; setPageType: Dispatch<`${PageTypeEnum}`>;
@ -28,7 +21,7 @@ interface RegisterType {
} }
const RegisterForm = ({ setPageType, loginSuccess }: Props) => { const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
const toast = useToast(); const { toast } = useToast();
const { mediaLgMd } = useScreen(); const { mediaLgMd } = useScreen();
const { const {
register, register,
@ -57,6 +50,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
async ({ email, code, password }: RegisterType) => { async ({ email, code, password }: RegisterType) => {
setRequesting(true); setRequesting(true);
try { try {
toast({
title: `密码已找回`,
status: 'success'
});
loginSuccess( loginSuccess(
await postFindPassword({ await postFindPassword({
email, email,
@ -64,11 +61,6 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
password password
}) })
); );
toast({
title: `密码已找回`,
status: 'success',
position: 'top'
});
} catch (error) { } catch (error) {
typeof error === 'string' && typeof error === 'string' &&
toast({ toast({

View File

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

View File

@ -1,19 +1,12 @@
import React, { useState, Dispatch, useCallback } from 'react'; import React, { useState, Dispatch, useCallback } from 'react';
import { import { FormControl, Box, Input, Button, FormErrorMessage, Flex } from '@chakra-ui/react';
FormControl,
Box,
Input,
Button,
FormErrorMessage,
useToast,
Flex
} from '@chakra-ui/react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { PageTypeEnum } from '@/constants/user'; import { PageTypeEnum } from '@/constants/user';
import { postRegister } from '@/api/user'; import { postRegister } from '@/api/user';
import { useSendCode } from '@/hooks/useSendCode'; import { useSendCode } from '@/hooks/useSendCode';
import type { ResLogin } from '@/api/response/user'; import type { ResLogin } from '@/api/response/user';
import { useScreen } from '@/hooks/useScreen'; import { useScreen } from '@/hooks/useScreen';
import { useToast } from '@/hooks/useToast';
interface Props { interface Props {
loginSuccess: (e: ResLogin) => void; loginSuccess: (e: ResLogin) => void;
@ -28,7 +21,7 @@ interface RegisterType {
} }
const RegisterForm = ({ setPageType, loginSuccess }: Props) => { const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
const toast = useToast(); const { toast } = useToast();
const { mediaLgMd } = useScreen(); const { mediaLgMd } = useScreen();
const { const {
register, register,
@ -57,6 +50,10 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
async ({ email, password, code }: RegisterType) => { async ({ email, password, code }: RegisterType) => {
setRequesting(true); setRequesting(true);
try { try {
toast({
title: `注册成功`,
status: 'success'
});
loginSuccess( loginSuccess(
await postRegister({ await postRegister({
email, email,
@ -64,17 +61,13 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
password password
}) })
); );
toast({
title: `注册成功`,
status: 'success',
position: 'top'
});
} catch (error) { } catch (error) {
typeof error === 'string' && typeof error === 'string' &&
toast({ toast({
title: error, title: error,
status: 'error', status: 'error',
position: 'top' duration: 4000,
isClosable: true
}); });
} }
setRequesting(false); setRequesting(false);

View File

@ -1,7 +1,5 @@
.loginPage { .loginPage {
background: url('/icon/login-bg.svg') no-repeat; background: url('/icon/login-bg.svg') no-repeat;
background-size: cover; background-size: cover;
height: 100vh;
width: 100vw;
user-select: none; user-select: none;
} }

View File

@ -40,7 +40,7 @@ const Login = () => {
}; };
return ( return (
<Box className={styles.loginPage} p={isPc ? '10vh 10vw' : 0}> <Box className={styles.loginPage} h={'100%'} p={isPc ? '10vh 10vw' : 0}>
<Flex <Flex
maxW={'1240px'} maxW={'1240px'}
m={'auto'} m={'auto'}

View File

@ -8,29 +8,23 @@ import { useRouter } from 'next/router';
import ModelTable from './components/ModelTable'; import ModelTable from './components/ModelTable';
import ModelPhoneList from './components/ModelPhoneList'; import ModelPhoneList from './components/ModelPhoneList';
import { useScreen } from '@/hooks/useScreen'; import { useScreen } from '@/hooks/useScreen';
import { useGlobalStore } from '@/store/global'; import { useQuery } from '@tanstack/react-query';
import { useLoading } from '@/hooks/useLoading';
const ModelList = () => { const ModelList = () => {
const { isPc } = useScreen(); const { isPc } = useScreen();
const router = useRouter(); const router = useRouter();
const [models, setModels] = useState<ModelType[]>([]); const [models, setModels] = useState<ModelType[]>([]);
const [openCreateModel, setOpenCreateModel] = useState(false); const [openCreateModel, setOpenCreateModel] = useState(false);
const { setLoading } = useGlobalStore(); const { Loading, setIsLoading } = useLoading();
/* 加载模型 */ /* 加载模型 */
const loadModels = useCallback(async () => { const { isLoading } = useQuery(['loadModels'], () => getMyModels(), {
setLoading(true); onSuccess(res) {
try { if (!res) return;
const res = await getMyModels();
setModels(res); setModels(res);
} catch (err) {
console.log(err);
} }
setLoading(false); });
}, [setLoading]);
useEffect(() => {
loadModels();
}, [loadModels]);
/* 创建成功回调 */ /* 创建成功回调 */
const createModelSuccess = useCallback((data: ModelType) => { const createModelSuccess = useCallback((data: ModelType) => {
@ -40,7 +34,7 @@ const ModelList = () => {
/* 点前往聊天预览页 */ /* 点前往聊天预览页 */
const handlePreviewChat = useCallback( const handlePreviewChat = useCallback(
async (modelId: string) => { async (modelId: string) => {
setLoading(true); setIsLoading(true);
try { try {
const chatId = await getChatSiteId(modelId); const chatId = await getChatSiteId(modelId);
@ -50,9 +44,9 @@ const ModelList = () => {
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
setLoading(false); setIsLoading(false);
}, },
[router, setLoading] [router, setIsLoading]
); );
return ( return (
@ -83,6 +77,7 @@ const ModelList = () => {
setCreateModelOpen={setOpenCreateModel} setCreateModelOpen={setOpenCreateModel}
onSuccess={createModelSuccess} onSuccess={createModelSuccess}
/> />
<Loading loading={isLoading} />
</Box> </Box>
); );
}; };

View File

@ -8,7 +8,7 @@ export async function connectToDatabase() {
return cachedClient; return cachedClient;
} }
cachedClient = await mongoose.connect(process.env.MONGODB_UR as string, { cachedClient = await mongoose.connect(process.env.MONGODB_URI as string, {
dbName: 'doc_gpt' dbName: 'doc_gpt'
}); });

View File

@ -45,14 +45,6 @@ pre,
samp { samp {
font-family: couriernew, courier, monospace; font-family: couriernew, courier, monospace;
} }
small {
font-size: 12px;
}
ul,
ol {
list-style: none;
padding: 0;
}
a { a {
text-decoration: none; text-decoration: none;
} }
@ -82,6 +74,9 @@ table {
border-collapse: collapse; border-collapse: collapse;
border-spacing: 0; border-spacing: 0;
} }
#__next {
height: 100%;
}
::-webkit-scrollbar, ::-webkit-scrollbar,
::-webkit-scrollbar { ::-webkit-scrollbar {

View File

@ -8,20 +8,26 @@ export const useCopyData = () => {
const { toast } = useToast(); const { toast } = useToast();
return { return {
copyData: (data: string, title: string = '复制成功') => { copyData: (data: string, title: string = '复制成功') => {
const clipboardObj = navigator.clipboard; try {
clipboardObj const textarea = document.createElement('textarea');
.writeText(data) textarea.value = data;
.then(() => { document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
toast({ toast({
title, title,
status: 'success', status: 'success',
duration: 1000 duration: 1000
}); });
}) } catch (error) {
.catch((err) => { console.log(error);
console.log(err); toast({
title: '复制失败',
status: 'error'
}); });
} }
}
}; };
}; };