Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
8e9816d6
authored
Jul 04, 2023
by
archer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
app page
parent
6e1ef89d
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
158 additions
and
174 deletions
+158
-174
client/src/components/Layout/navbar.tsx
+1
-1
client/src/components/SlideTabs/index.tsx
+72
-0
client/src/constants/theme.ts
+2
-1
client/src/pages/api/app/create.ts
+1
-1
client/src/pages/api/app/update.ts
+1
-1
client/src/pages/app/detail/components/Settings.tsx
+5
-123
client/src/pages/app/detail/index.tsx
+76
-47
No files found.
client/src/components/Layout/navbar.tsx
View file @
8e9816d6
...
@@ -30,7 +30,7 @@ const Navbar = ({ unread }: { unread: number }) => {
...
@@ -30,7 +30,7 @@ const Navbar = ({ unread }: { unread: number }) => {
label
:
'应用'
,
label
:
'应用'
,
icon
:
'model'
,
icon
:
'model'
,
link
:
`/app/list`
,
link
:
`/app/list`
,
activeLink
:
[
'/app/list'
]
activeLink
:
[
'/app/list'
,
'/app/detail'
]
},
},
{
{
label
:
'知识库'
,
label
:
'知识库'
,
...
...
client/src/components/SlideTabs/index.tsx
0 → 100644
View file @
8e9816d6
import
React
,
{
useMemo
}
from
'react'
;
import
{
Box
,
Flex
,
useTheme
}
from
'@chakra-ui/react'
;
import
type
{
GridProps
}
from
'@chakra-ui/react'
;
import
MyIcon
,
{
type
IconName
}
from
'../Icon'
;
// @ts-ignore
export
interface
Props
extends
GridProps
{
list
:
{
id
:
string
;
label
:
string
;
icon
:
string
}[];
activeId
:
string
;
size
?:
'sm'
|
'md'
|
'lg'
;
onChange
:
(
id
:
string
)
=>
void
;
}
const
SlideTabs
=
({
list
,
size
=
'md'
,
activeId
,
onChange
,
...
props
}:
Props
)
=>
{
const
sizeMap
=
useMemo
(()
=>
{
switch
(
size
)
{
case
'sm'
:
return
{
fontSize
:
'sm'
,
inlineP
:
1
};
case
'md'
:
return
{
fontSize
:
'md'
,
inlineP
:
2
};
case
'lg'
:
return
{
fontSize
:
'lg'
,
inlineP
:
3
};
}
},
[
size
]);
return
(
<
Box
fontSize=
{
sizeMap
.
fontSize
}
{
...
props
}
>
{
list
.
map
((
item
)
=>
(
<
Flex
key=
{
item
.
id
}
py=
{
sizeMap
.
inlineP
}
borderRadius=
{
'md'
}
px=
{
3
}
mb=
{
2
}
alignItems=
{
'center'
}
_hover=
{
{
bg
:
'myWhite.600'
}
}
{
...
(
activeId
===
item
.
id
?
{
// backgroundImage: 'linear-gradient(to right, #85b1ff 0%, #EBF7FD 100%)',
bg
:
'
myBlue
.
300',
fontWeight
:
'
bold
',
color
:
'
myBlue
.
700',
cursor
:
'
default
'
}
:
{
cursor
:
'
pointer
'
})}
onClick=
{
()
=>
{
if
(
activeId
===
item
.
id
)
return
;
onChange
(
item
.
id
);
}
}
>
<
MyIcon
mr=
{
2
}
name=
{
item
.
icon
as
IconName
}
w=
{
'14px'
}
/>
{
item
.
label
}
</
Flex
>
))
}
</
Box
>
);
};
export
default
React
.
memo
(
SlideTabs
);
client/src/constants/theme.ts
View file @
8e9816d6
...
@@ -274,7 +274,8 @@ export const theme = extendTheme({
...
@@ -274,7 +274,8 @@ export const theme = extendTheme({
borders
:
{
borders
:
{
sm
:
'1px solid #EFF0F1'
,
sm
:
'1px solid #EFF0F1'
,
base
:
'1px solid #DEE0E2'
,
base
:
'1px solid #DEE0E2'
,
md
:
'1px solid #DAE0E2'
md
:
'1px solid #DAE0E2'
,
lg
:
'1px solid #D0E0E2'
},
},
breakpoints
:
{
breakpoints
:
{
sm
:
'900px'
,
sm
:
'900px'
,
...
...
client/src/pages/api/app/create.ts
View file @
8e9816d6
...
@@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
...
@@ -3,7 +3,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import
{
jsonRes
}
from
'@/service/response'
;
import
{
jsonRes
}
from
'@/service/response'
;
import
{
connectToDatabase
}
from
'@/service/mongo'
;
import
{
connectToDatabase
}
from
'@/service/mongo'
;
import
{
authUser
}
from
'@/service/utils/auth'
;
import
{
authUser
}
from
'@/service/utils/auth'
;
import
{
App
}
from
'@/service/models/
model
'
;
import
{
App
}
from
'@/service/models/
app
'
;
export
default
async
function
handler
(
req
:
NextApiRequest
,
res
:
NextApiResponse
<
any
>
)
{
export
default
async
function
handler
(
req
:
NextApiRequest
,
res
:
NextApiResponse
<
any
>
)
{
try
{
try
{
...
...
client/src/pages/api/app/update.ts
View file @
8e9816d6
...
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
...
@@ -2,7 +2,7 @@ import type { NextApiRequest, NextApiResponse } from 'next';
import
{
jsonRes
}
from
'@/service/response'
;
import
{
jsonRes
}
from
'@/service/response'
;
import
{
connectToDatabase
}
from
'@/service/mongo'
;
import
{
connectToDatabase
}
from
'@/service/mongo'
;
import
{
authUser
}
from
'@/service/utils/auth'
;
import
{
authUser
}
from
'@/service/utils/auth'
;
import
{
App
}
from
'@/service/models/
model
'
;
import
{
App
}
from
'@/service/models/
app
'
;
import
type
{
AppUpdateParams
}
from
'@/types/app'
;
import
type
{
AppUpdateParams
}
from
'@/types/app'
;
import
{
authApp
}
from
'@/service/utils/auth'
;
import
{
authApp
}
from
'@/service/utils/auth'
;
...
...
client/src/pages/app/detail/components/Settings.tsx
View file @
8e9816d6
import
React
,
{
useCallback
,
useState
,
useMemo
}
from
'react'
;
import
React
,
{
useCallback
,
useState
,
useMemo
}
from
'react'
;
import
{
import
{
Box
,
Flex
,
Button
,
FormControl
,
Input
,
Textarea
,
Divider
}
from
'@chakra-ui/react'
;
Box
,
Flex
,
Button
,
FormControl
,
Input
,
Textarea
,
Divider
,
Tooltip
}
from
'@chakra-ui/react'
;
import
{
QuestionOutlineIcon
}
from
'@chakra-ui/icons'
;
import
{
useQuery
}
from
'@tanstack/react-query'
;
import
{
useQuery
}
from
'@tanstack/react-query'
;
import
{
useForm
}
from
'react-hook-form'
;
import
{
useForm
}
from
'react-hook-form'
;
import
{
useRouter
}
from
'next/router'
;
import
{
useRouter
}
from
'next/router'
;
...
@@ -21,19 +11,10 @@ import { useSelectFile } from '@/hooks/useSelectFile';
...
@@ -21,19 +11,10 @@ import { useSelectFile } from '@/hooks/useSelectFile';
import
{
compressImg
}
from
'@/utils/file'
;
import
{
compressImg
}
from
'@/utils/file'
;
import
{
getErrText
}
from
'@/utils/tools'
;
import
{
getErrText
}
from
'@/utils/tools'
;
import
{
useConfirm
}
from
'@/hooks/useConfirm'
;
import
{
useConfirm
}
from
'@/hooks/useConfirm'
;
import
{
ChatModelMap
,
getChatModelList
}
from
'@/constants/model'
;
import
{
formatPrice
}
from
'@/utils/user'
;
import
type
{
AppSchema
}
from
'@/types/mongoSchema'
;
import
type
{
AppSchema
}
from
'@/types/mongoSchema'
;
import
Avatar
from
'@/components/Avatar'
;
import
Avatar
from
'@/components/Avatar'
;
import
MySelect
from
'@/components/Select'
;
import
MySlider
from
'@/components/Slider'
;
const
systemPromptTip
=
'模型固定的引导词,通过调整该内容,可以引导模型聊天方向。该内容会被固定在上下文的开头。'
;
const
limitPromptTip
=
'限定模型对话范围,会被放置在本次提问前,拥有强引导和限定性。例如:\n1. 知识库是关于 Laf 的介绍,参考知识库回答问题,与 "Laf" 无关内容,直接回复: "我不知道"。\n2. 你仅回答关于 "xxx" 的问题,其他问题回复: "xxxx"'
;
const
Settings
=
({
modelId
}:
{
modelId
:
string
})
=>
{
const
Settings
=
({
modelId
}:
{
modelId
:
string
})
=>
{
const
{
toast
}
=
useToast
();
const
{
toast
}
=
useToast
();
...
@@ -67,15 +48,6 @@ const Settings = ({ modelId }: { modelId: string }) => {
...
@@ -67,15 +48,6 @@ const Settings = ({ modelId }: { modelId: string }) => {
()
=>
appDetail
.
userId
===
userInfo
?.
_id
,
()
=>
appDetail
.
userId
===
userInfo
?.
_id
,
[
appDetail
.
userId
,
userInfo
?.
_id
]
[
appDetail
.
userId
,
userInfo
?.
_id
]
);
);
const
tokenLimit
=
useMemo
(()
=>
{
const
max
=
ChatModelMap
[
getValues
(
'chat.chatModel'
)]?.
contextMaxToken
||
4000
;
if
(
max
<
getValues
(
'chat.maxToken'
))
{
setValue
(
'chat.maxToken'
,
max
);
}
return
max
;
},
[
getValues
,
setValue
,
refresh
]);
// 提交保存模型修改
// 提交保存模型修改
const
saveSubmitSuccess
=
useCallback
(
const
saveSubmitSuccess
=
useCallback
(
...
@@ -185,17 +157,17 @@ const Settings = ({ modelId }: { modelId: string }) => {
...
@@ -185,17 +157,17 @@ const Settings = ({ modelId }: { modelId: string }) => {
}
}
});
});
const { data: chatModelList = [] } = useQuery(['initChatModelList'], getChatModelList);
return (
return (
<Box
<Box
pt={[0, 5]}
pb={3}
pb={3}
px={[5, '25px', '50px']}
px={[5, '25px', '50px']}
fontSize={['sm', 'lg']}
fontSize={['sm', 'lg']}
maxW={['auto', '800px']}
position={'relative'}
position={'relative'}
maxW={['auto', '800px']}
>
>
<Flex alignItems={'center'}>
<Box fontSize={['md', 'xl']}>基本信息</Box>
<Flex mt={5} alignItems={'center'}>
<Box w={['60px', '100px', '140px']} flexShrink={0}>
<Box w={['60px', '100px', '140px']} flexShrink={0}>
头像
头像
</Box>
</Box>
...
@@ -235,96 +207,6 @@ const Settings = ({ modelId }: { modelId: string }) => {
...
@@ -235,96 +207,6 @@ const Settings = ({ modelId }: { modelId: string }) => {
<Divider mt={5} />
<Divider mt={5} />
<Flex alignItems={'center'} mt={5}>
<Box w={['60px', '100px', '140px']} flexShrink={0}>
对话模型
</Box>
<MySelect
width={['90%', '280px']}
value={getValues('chat.chatModel')}
list={chatModelList.map((item) => ({
value: item.chatModel,
label: `
$
{
item
.
name
}
(
$
{
formatPrice
(
ChatModelMap
[
item
.
chatModel
]?.
price
,
1000
)}
元
/
1
k
tokens
)
`
}))}
onchange={(val: any) => {
setValue('chat.chatModel', val);
setRefresh(!refresh);
}}
/>
</Flex>
<Flex alignItems={'center'} my={10}>
<Box w={['60px', '100px', '140px']} flexShrink={0}>
温度
</Box>
<Box flex={1} ml={'10px'}>
<MySlider
markList={[
{ label: '严谨', value: 0 },
{ label: '发散', value: 10 }
]}
width={['90%', '260px']}
min={0}
max={10}
value={getValues('chat.temperature')}
onChange={(val) => {
setValue('chat.temperature', val);
setRefresh(!refresh);
}}
/>
</Box>
</Flex>
<Flex alignItems={'center'} mt={12} mb={10}>
<Box w={['60px', '100px', '140px']} flexShrink={0}>
回复上限
</Box>
<Box flex={1} ml={'10px'}>
<MySlider
markList={[
{ label: '100', value: 100 },
{ label: `
$
{
tokenLimit
}
`, value: tokenLimit }
]}
width={['90%', '260px']}
min={100}
max={tokenLimit}
step={50}
value={getValues('chat.maxToken')}
onChange={(val) => {
setValue('chat.maxToken', val);
setRefresh(!refresh);
}}
/>
</Box>
</Flex>
<Flex mt={10} alignItems={'flex-start'}>
<Box w={['60px', '100px', '140px']} flexShrink={0}>
提示词
<Tooltip label={systemPromptTip}>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</Tooltip>
</Box>
<Textarea
rows={8}
placeholder={systemPromptTip}
{...register('chat.systemPrompt')}
></Textarea>
</Flex>
<Flex mt={5} alignItems={'flex-start'}>
<Box w={['60px', '100px', '140px']} flexShrink={0}>
限定词
<Tooltip label={limitPromptTip}>
<QuestionOutlineIcon display={['none', 'inline']} ml={1} />
</Tooltip>
</Box>
<Textarea
rows={5}
placeholder={limitPromptTip}
{...register('chat.limitPrompt')}
></Textarea>
</Flex>
<Flex mt={5} alignItems={'center'}>
<Flex mt={5} alignItems={'center'}>
<Box w={['60px', '100px', '140px']} flexShrink={0}></Box>
<Box w={['60px', '100px', '140px']} flexShrink={0}></Box>
<Button
<Button
...
...
client/src/pages/app/detail/index.tsx
View file @
8e9816d6
import
React
,
{
useState
,
useEffect
,
useMemo
,
useCallback
}
from
'react'
;
import
React
,
{
useState
,
useEffect
,
useMemo
,
useCallback
,
useRef
}
from
'react'
;
import
{
useRouter
}
from
'next/router'
;
import
{
useRouter
}
from
'next/router'
;
import
{
Box
,
Flex
}
from
'@chakra-ui/react'
;
import
{
Box
,
Flex
,
useTheme
}
from
'@chakra-ui/react'
;
import
{
useUserStore
}
from
'@/store/user'
;
import
{
useUserStore
}
from
'@/store/user'
;
import
{
useGlobalStore
}
from
'@/store/global'
;
import
dynamic
from
'next/dynamic'
;
import
dynamic
from
'next/dynamic'
;
import
Tabs
from
'@/components/Tabs'
;
import
Tabs
from
'@/components/Tabs'
;
import
SlideTabs
from
'@/components/SlideTabs'
;
import
Settings
from
'./components/Settings'
;
import
Settings
from
'./components/Settings'
;
import
{
defaultApp
}
from
'@/constants/model'
;
import
{
defaultApp
}
from
'@/constants/model'
;
import
Avatar
from
'@/components/Avatar'
;
const
EditApp
=
dynamic
(()
=>
import
(
'./components/edit'
),
{
const
EditApp
=
dynamic
(()
=>
import
(
'./components/edit'
),
{
ssr
:
false
ssr
:
false
...
@@ -28,8 +29,8 @@ enum TabEnum {
...
@@ -28,8 +29,8 @@ enum TabEnum {
const
AppDetail
=
({
currentTab
}:
{
currentTab
:
`
${
TabEnum
}
`
})
=>
{
const
AppDetail
=
({
currentTab
}:
{
currentTab
:
`
${
TabEnum
}
`
})
=>
{
const
router
=
useRouter
();
const
router
=
useRouter
();
const
theme
=
useTheme
();
const
{
appId
}
=
router
.
query
as
{
appId
:
string
};
const
{
appId
}
=
router
.
query
as
{
appId
:
string
};
const
{
isPc
}
=
useGlobalStore
();
const
{
appDetail
=
defaultApp
,
loadAppDetail
,
userInfo
}
=
useUserStore
();
const
{
appDetail
=
defaultApp
,
loadAppDetail
,
userInfo
}
=
useUserStore
();
const
isOwner
=
useMemo
(
const
isOwner
=
useMemo
(
...
@@ -50,6 +51,17 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
...
@@ -50,6 +51,17 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
[
appId
,
router
]
[
appId
,
router
]
);
);
const
tabList
=
useMemo
(
()
=>
[
{
label
:
'基础信息'
,
id
:
TabEnum
.
settings
,
icon
:
'text'
},
...(
isOwner
?
[{
label
:
'编排'
,
id
:
TabEnum
.
edit
,
icon
:
'edit'
}]
:
[]),
{
label
:
'分享'
,
id
:
TabEnum
.
share
,
icon
:
'edit'
},
{
label
:
'API'
,
id
:
TabEnum
.
API
,
icon
:
'edit'
},
{
label
:
'立即对话'
,
id
:
'startChat'
,
icon
:
'chat'
}
],
[
isOwner
]
);
useEffect
(()
=>
{
useEffect
(()
=>
{
window
.
onbeforeunload
=
(
e
)
=>
{
window
.
onbeforeunload
=
(
e
)
=>
{
e
.
preventDefault
();
e
.
preventDefault
();
...
@@ -66,51 +78,68 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
...
@@ -66,51 +78,68 @@ const AppDetail = ({ currentTab }: { currentTab: `${TabEnum}` }) => {
},
[
appId
,
loadAppDetail
]);
},
[
appId
,
loadAppDetail
]);
return
(
return
(
<
Flex
<
Flex
flexDirection=
{
'column'
}
bg=
{
'myGray.100'
}
h=
{
'100%'
}
p=
{
[
0
,
5
]
}
>
flexDirection=
{
'column'
}
<
Box
h=
{
'100%'
}
display=
{
[
'block'
,
'flex'
]
}
maxW=
{
'100vw'
}
flex=
{
1
}
pt=
{
4
}
bg=
{
'white'
}
overflow=
{
'overlay'
}
borderRadius=
{
[
''
,
'2xl'
]
}
position=
{
'relative'
}
border=
{
[
''
,
theme
.
borders
.
lg
]
}
bg=
{
'white'
}
>
>
{
/* pc tab */
}
{
/* 头部 */
}
<
Box
display=
{
[
'none'
,
'block'
]
}
p=
{
4
}
w=
{
'200px'
}
borderRight=
{
theme
.
borders
.
base
}
>
<
Box
textAlign=
{
[
'center'
,
'left'
]
}
px=
{
5
}
mb=
{
4
}
>
<
Flex
mb=
{
4
}
>
<
Box
className=
"textlg"
display=
{
[
'block'
,
'none'
]
}
fontSize=
{
'3xl'
}
fontWeight=
{
'bold'
}
>
<
Avatar
src=
{
appDetail
.
avatar
}
w=
{
'34px'
}
borderRadius=
{
'lg'
}
/>
{
appDetail
.
name
}
<
Box
ml=
{
2
}
fontWeight=
{
'bold'
}
>
{
appDetail
.
name
}
</
Box
>
</
Flex
>
<
SlideTabs
mx=
{
'auto'
}
mt=
{
2
}
w=
{
'100%'
}
list=
{
tabList
}
activeId=
{
currentTab
}
onChange=
{
(
e
:
any
)
=>
{
if
(
e
===
'startChat'
)
{
router
.
push
(
`/chat?modelId=${appId}`
);
}
else
{
setCurrentTab
(
e
);
}
}
}
/>
</
Box
>
</
Box
>
<
Tabs
{
/* phone tab */
}
mx=
{
[
'auto'
,
'0'
]
}
<
Box
display=
{
[
'block'
,
'none'
]
}
textAlign=
{
'center'
}
px=
{
5
}
py=
{
3
}
>
mt=
{
2
}
<
Box
className=
"textlg"
fontSize=
{
'3xl'
}
fontWeight=
{
'bold'
}
>
w=
{
[
'300px'
,
'360px'
]
}
{
appDetail
.
name
}
list=
{
[
{
label
:
'配置'
,
id
:
TabEnum
.
settings
},
...(
isOwner
?
[{
label
:
'编排'
,
id
:
TabEnum
.
edit
}]
:
[]),
{
label
:
'分享'
,
id
:
TabEnum
.
share
},
{
label
:
'API'
,
id
:
TabEnum
.
API
},
{
label
:
'立即对话'
,
id
:
'startChat'
}
]
}
size=
{
isPc
?
'md'
:
'sm'
}
activeId=
{
currentTab
}
onChange=
{
(
e
:
any
)
=>
{
if
(
e
===
'startChat'
)
{
router
.
push
(
`/chat?modelId=${appId}`
);
}
else
{
setCurrentTab
(
e
);
}
}
}
/>
</
Box
>
<
Box
flex=
{
1
}
>
{
currentTab
===
TabEnum
.
settings
&&
<
Settings
modelId=
{
appId
}
/>
}
{
currentTab
===
TabEnum
.
edit
&&
(
<
Box
position=
{
'fixed'
}
zIndex=
{
999
}
top=
{
0
}
left=
{
0
}
right=
{
0
}
bottom=
{
0
}
>
<
EditApp
app=
{
appDetail
}
onBack=
{
()
=>
setCurrentTab
(
TabEnum
.
settings
)
}
/>
</
Box
>
</
Box
>
)
}
<
Tabs
{
currentTab
===
TabEnum
.
API
&&
<
API
modelId=
{
appId
}
/>
}
mx=
{
'auto'
}
{
currentTab
===
TabEnum
.
share
&&
<
Share
modelId=
{
appId
}
/>
}
mt=
{
2
}
w=
{
'300px'
}
list=
{
tabList
}
size=
{
'sm'
}
activeId=
{
currentTab
}
onChange=
{
(
e
:
any
)
=>
{
if
(
e
===
'startChat'
)
{
router
.
push
(
`/chat?modelId=${appId}`
);
}
else
{
setCurrentTab
(
e
);
}
}
}
/>
</
Box
>
<
Box
flex=
{
1
}
>
{
currentTab
===
TabEnum
.
settings
&&
<
Settings
modelId=
{
appId
}
/>
}
{
currentTab
===
TabEnum
.
edit
&&
(
<
Box
position=
{
'fixed'
}
zIndex=
{
999
}
top=
{
0
}
left=
{
0
}
right=
{
0
}
bottom=
{
0
}
>
<
EditApp
app=
{
appDetail
}
onBack=
{
()
=>
setCurrentTab
(
TabEnum
.
settings
)
}
/>
</
Box
>
)
}
{
currentTab
===
TabEnum
.
API
&&
<
API
modelId=
{
appId
}
/>
}
{
currentTab
===
TabEnum
.
share
&&
<
Share
modelId=
{
appId
}
/>
}
</
Box
>
</
Box
>
</
Box
>
</
Flex
>
</
Flex
>
);
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment