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
6ac7119e
authored
Jun 12, 2023
by
archer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: kb UI
parent
daf1148b
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
162 additions
and
160 deletions
+162
-160
README.md
+1
-1
client/src/components/Tag/index.tsx
+3
-3
client/src/constants/theme.ts
+2
-2
client/src/hooks/usePagination.tsx
+3
-1
client/src/pages/api/plugins/kb/data/getDataList.ts
+1
-0
client/src/pages/kb/components/DataCard.tsx
+74
-96
client/src/pages/kb/components/Detail.tsx
+29
-10
client/src/pages/kb/components/Info.tsx
+45
-44
client/src/pages/kb/components/KbList.tsx
+2
-2
client/src/pages/number/index.tsx
+2
-1
No files found.
README.md
View file @
6ac7119e
...
@@ -37,8 +37,8 @@ Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接
...
@@ -37,8 +37,8 @@ Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接
## 👀 其他
## 👀 其他
-
[
FastGpt 常见问题
](
https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh
)
-
[
FastGpt 常见问题
](
https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh
)
-
[
docker 部署教程
](
https://www.bilibili.com/video/BV1jo4y147fT/
)
-
[
公众号接入
](
https://www.bilibili.com/video/BV1xh4y1t7fy/
)
-
[
公众号接入
](
https://www.bilibili.com/video/BV1xh4y1t7fy/
)
-
[
FastGpt + Laf 最佳实践,将知识库装入公众号,点击去 Laf 公众号体验效果
](
https://b4jky7-fastgpt.oss.laf.run/lafercode.png
)
-
[
FastGpt V3.4 更新集合
](
https://www.bilibili.com/video/BV1Lo4y147Qh/?vd_source=92041a1a395f852f9d89158eaa3f61b4
)
-
[
FastGpt V3.4 更新集合
](
https://www.bilibili.com/video/BV1Lo4y147Qh/?vd_source=92041a1a395f852f9d89158eaa3f61b4
)
-
[
FastGpt 知识库演示
](
https://www.bilibili.com/video/BV1Wo4y1p7i1/
)
-
[
FastGpt 知识库演示
](
https://www.bilibili.com/video/BV1Wo4y1p7i1/
)
...
...
client/src/components/Tag/index.tsx
View file @
6ac7119e
...
@@ -29,11 +29,11 @@ const Tag = ({ children, colorSchema = 'blue', ...props }: Props) => {
...
@@ -29,11 +29,11 @@ const Tag = ({ children, colorSchema = 'blue', ...props }: Props) => {
},
[
colorSchema
]);
},
[
colorSchema
]);
return
(
return
(
<
Box
<
Box
as=
{
'span
'
}
display=
{
'inline-block
'
}
border=
{
'1px solid'
}
border=
{
'1px solid'
}
px=
{
2
}
px=
{
2
}
lineHeight=
{
0
}
lineHeight=
{
1
}
py=
{
'
1
px'
}
py=
{
'
2
px'
}
borderRadius=
{
'md'
}
borderRadius=
{
'md'
}
fontSize=
{
'xs'
}
fontSize=
{
'xs'
}
{
...
theme
}
{
...
theme
}
...
...
client/src/constants/theme.ts
View file @
6ac7119e
...
@@ -249,8 +249,8 @@ export const theme = extendTheme({
...
@@ -249,8 +249,8 @@ export const theme = extendTheme({
'2xl'
:
'2100px'
'2xl'
:
'2100px'
},
},
lgColor
:
{
lgColor
:
{
activeBlueGradient
:
'linear-gradient(
120deg
, #d6e8ff 0%, #f0f7ff 100%)'
,
activeBlueGradient
:
'linear-gradient(
to bottom right
, #d6e8ff 0%, #f0f7ff 100%)'
,
hoverBlueGradient
:
'linear-gradient(
60deg, #f0f7ff 0%, #d6e8
ff 100%)'
,
hoverBlueGradient
:
'linear-gradient(
to top left, #d6e8ff 0%, #f0f7
ff 100%)'
,
primary
:
'linear-gradient(to bottom right, #2152d9 0%,#3370ff 40%, #4e83fd 100%)'
,
primary
:
'linear-gradient(to bottom right, #2152d9 0%,#3370ff 40%, #4e83fd 100%)'
,
primary2
:
'linear-gradient(to bottom right, #2152d9 0%,#3370ff 30%,#4e83fd 80%, #85b1ff 100%)'
primary2
:
'linear-gradient(to bottom right, #2152d9 0%,#3370ff 30%,#4e83fd 80%, #85b1ff 100%)'
},
},
...
...
client/src/hooks/usePagination.tsx
View file @
6ac7119e
...
@@ -54,6 +54,7 @@ export const usePagination = <T = any,>({
...
@@ -54,6 +54,7 @@ export const usePagination = <T = any,>({
size=
{
'sm'
}
size=
{
'sm'
}
w=
{
'28px'
}
w=
{
'28px'
}
h=
{
'28px'
}
h=
{
'28px'
}
isLoading=
{
isLoading
}
onClick=
{
()
=>
mutate
(
pageNum
-
1
)
}
onClick=
{
()
=>
mutate
(
pageNum
-
1
)
}
/>
/>
<
Flex
mx=
{
2
}
alignItems=
{
'center'
}
>
<
Flex
mx=
{
2
}
alignItems=
{
'center'
}
>
...
@@ -84,13 +85,14 @@ export const usePagination = <T = any,>({
...
@@ -84,13 +85,14 @@ export const usePagination = <T = any,>({
icon=
{
<
ArrowForwardIcon
/>
}
icon=
{
<
ArrowForwardIcon
/>
}
aria
-
label=
{
'left'
}
aria
-
label=
{
'left'
}
size=
{
'sm'
}
size=
{
'sm'
}
isLoading=
{
isLoading
}
w=
{
'28px'
}
w=
{
'28px'
}
h=
{
'28px'
}
h=
{
'28px'
}
onClick=
{
()
=>
mutate
(
pageNum
+
1
)
}
onClick=
{
()
=>
mutate
(
pageNum
+
1
)
}
/>
/>
</
Flex
>
</
Flex
>
);
);
},
[
maxPage
,
mutate
,
pageNum
]);
},
[
isLoading
,
maxPage
,
mutate
,
pageNum
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
defaultRequest
&&
mutate
(
1
);
defaultRequest
&&
mutate
(
1
);
...
...
client/src/pages/api/plugins/kb/data/getDataList.ts
View file @
6ac7119e
...
@@ -26,6 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
...
@@ -26,6 +26,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const
{
userId
}
=
await
authUser
({
req
,
authToken
:
true
});
const
{
userId
}
=
await
authUser
({
req
,
authToken
:
true
});
await
connectToDatabase
();
await
connectToDatabase
();
searchText
=
searchText
.
replace
(
/'/g
,
''
);
const
where
:
any
=
[
const
where
:
any
=
[
[
'user_id'
,
userId
],
[
'user_id'
,
userId
],
...
...
client/src/pages/kb/components/DataCard.tsx
View file @
6ac7119e
import
React
,
{
useCallback
,
useState
,
useRef
,
useEffect
}
from
'react'
;
import
React
,
{
useCallback
,
useState
,
useRef
}
from
'react'
;
import
{
import
{
Box
,
Box
,
TableContainer
,
Card
,
Table
,
Thead
,
Tbody
,
Tr
,
Th
,
Td
,
IconButton
,
IconButton
,
Flex
,
Flex
,
Button
,
Button
,
...
@@ -17,10 +11,8 @@ import {
...
@@ -17,10 +11,8 @@ import {
MenuList
,
MenuList
,
MenuItem
,
MenuItem
,
Input
,
Input
,
Tooltip
Grid
}
from
'@chakra-ui/react'
;
}
from
'@chakra-ui/react'
;
import
{
QuestionOutlineIcon
}
from
'@chakra-ui/icons'
;
import
type
{
BoxProps
}
from
'@chakra-ui/react'
;
import
type
{
KbDataItemType
}
from
'@/types/plugin'
;
import
type
{
KbDataItemType
}
from
'@/types/plugin'
;
import
{
usePagination
}
from
'@/hooks/usePagination'
;
import
{
usePagination
}
from
'@/hooks/usePagination'
;
import
{
import
{
...
@@ -29,30 +21,21 @@ import {
...
@@ -29,30 +21,21 @@ import {
delOneKbDataByDataId
,
delOneKbDataByDataId
,
getTrainingData
getTrainingData
}
from
'@/api/plugins/kb'
;
}
from
'@/api/plugins/kb'
;
import
{
DeleteIcon
,
RepeatIcon
,
EditIcon
}
from
'@chakra-ui/icons'
;
import
{
DeleteIcon
}
from
'@chakra-ui/icons'
;
import
{
useLoading
}
from
'@/hooks/useLoading'
;
import
{
fileDownload
}
from
'@/utils/file'
;
import
{
fileDownload
}
from
'@/utils/file'
;
import
{
useMutation
,
useQuery
}
from
'@tanstack/react-query'
;
import
{
useMutation
,
useQuery
}
from
'@tanstack/react-query'
;
import
{
useToast
}
from
'@/hooks/useToast'
;
import
{
useToast
}
from
'@/hooks/useToast'
;
import
Papa
from
'papaparse'
;
import
Papa
from
'papaparse'
;
import
dynamic
from
'next/dynamic'
;
import
dynamic
from
'next/dynamic'
;
import
InputModal
,
{
FormData
as
InputDataType
}
from
'./InputDataModal'
;
import
InputModal
,
{
FormData
as
InputDataType
}
from
'./InputDataModal'
;
import
{
debounce
}
from
'lodash'
;
const
SelectFileModal
=
dynamic
(()
=>
import
(
'./SelectFileModal'
));
const
SelectFileModal
=
dynamic
(()
=>
import
(
'./SelectFileModal'
));
const
SelectCsvModal
=
dynamic
(()
=>
import
(
'./SelectCsvModal'
));
const
SelectCsvModal
=
dynamic
(()
=>
import
(
'./SelectCsvModal'
));
const
DataCard
=
({
kbId
}:
{
kbId
:
string
})
=>
{
const
DataCard
=
({
kbId
}:
{
kbId
:
string
})
=>
{
const
lastSearch
=
useRef
(
''
);
const
lastSearch
=
useRef
(
''
);
const
tdStyles
=
useRef
<
BoxProps
>
({
fontSize
:
'xs'
,
minW
:
'150px'
,
maxW
:
'500px'
,
maxH
:
'250px'
,
whiteSpace
:
'pre-wrap'
,
overflowY
:
'auto'
});
const
[
searchText
,
setSearchText
]
=
useState
(
''
);
const
[
searchText
,
setSearchText
]
=
useState
(
''
);
const
{
Loading
,
setIsLoading
}
=
useLoading
();
const
{
toast
}
=
useToast
();
const
{
toast
}
=
useToast
();
const
{
const
{
...
@@ -64,7 +47,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
...
@@ -64,7 +47,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
pageNum
pageNum
}
=
usePagination
<
KbDataItemType
>
({
}
=
usePagination
<
KbDataItemType
>
({
api
:
getKbDataList
,
api
:
getKbDataList
,
pageSize
:
10
,
pageSize
:
24
,
params
:
{
params
:
{
kbId
,
kbId
,
searchText
searchText
...
@@ -109,7 +92,6 @@ const DataCard = ({ kbId }: { kbId: string }) => {
...
@@ -109,7 +92,6 @@ const DataCard = ({ kbId }: { kbId: string }) => {
mutationFn
:
()
=>
getExportDataList
(
kbId
),
mutationFn
:
()
=>
getExportDataList
(
kbId
),
onSuccess
(
res
)
{
onSuccess
(
res
)
{
try
{
try
{
setIsLoading
(
true
);
const
text
=
Papa
.
unparse
({
const
text
=
Papa
.
unparse
({
fields
:
[
'question'
,
'answer'
],
fields
:
[
'question'
,
'answer'
],
data
:
res
data
:
res
...
@@ -126,7 +108,6 @@ const DataCard = ({ kbId }: { kbId: string }) => {
...
@@ -126,7 +108,6 @@ const DataCard = ({ kbId }: { kbId: string }) => {
}
catch
(
error
)
{
}
catch
(
error
)
{
error
;
error
;
}
}
setIsLoading
(
false
);
},
},
onError
(
err
:
any
)
{
onError
(
err
:
any
)
{
toast
({
toast
({
...
@@ -137,6 +118,14 @@ const DataCard = ({ kbId }: { kbId: string }) => {
...
@@ -137,6 +118,14 @@ const DataCard = ({ kbId }: { kbId: string }) => {
}
}
});
});
const
getFirstData
=
useCallback
(
debounce
(()
=>
{
getData
(
1
);
lastSearch
.
current
=
searchText
;
},
300
),
[]
);
// interval get data
// interval get data
useQuery
([
'refetchData'
],
()
=>
refetchData
(
1
),
{
useQuery
([
'refetchData'
],
()
=>
refetchData
(
1
),
{
refetchInterval
:
5000
,
refetchInterval
:
5000
,
...
@@ -150,33 +139,23 @@ const DataCard = ({ kbId }: { kbId: string }) => {
...
@@ -150,33 +139,23 @@ const DataCard = ({ kbId }: { kbId: string }) => {
return
(
return
(
<
Box
position=
{
'relative'
}
>
<
Box
position=
{
'relative'
}
>
<
Flex
>
<
Flex
justifyContent=
{
'space-between'
}
>
<
Box
fontWeight=
{
'bold'
}
fontSize=
{
'lg'
}
flex=
{
1
}
mr=
{
2
}
>
<
Box
fontWeight=
{
'bold'
}
fontSize=
{
'lg'
}
mr=
{
2
}
>
知识库数据:
{
total
}
组
知识库数据:
{
total
}
组
</
Box
>
</
Box
>
<
IconButton
<
Box
>
icon=
{
<
RepeatIcon
/>
}
aria
-
label=
{
'refresh'
}
variant=
{
'base'
}
mr=
{
[
2
,
4
]
}
size=
{
'sm'
}
onClick=
{
()
=>
{
refetchData
(
pageNum
);
getTrainingData
({
kbId
,
init
:
true
});
}
}
/>
<
Button
<
Button
variant=
{
'base'
}
variant=
{
'base'
}
mr=
{
2
}
mr=
{
2
}
size=
{
'sm'
}
size=
{
'sm'
}
isLoading=
{
isLoadingExport
}
isLoading=
{
isLoadingExport
||
isLoading
}
title=
{
'半小时仅能导出1次'
}
title=
{
'半小时仅能导出1次'
}
onClick=
{
()
=>
onclickExport
()
}
onClick=
{
()
=>
onclickExport
()
}
>
>
导出csv
导出csv
</
Button
>
</
Button
>
<
Menu
autoSelect=
{
false
}
>
<
Menu
autoSelect=
{
false
}
>
<
MenuButton
as=
{
Button
}
size=
{
'sm'
}
>
<
MenuButton
as=
{
Button
}
size=
{
'sm'
}
isLoading=
{
isLoading
}
>
导入
导入
</
MenuButton
>
</
MenuButton
>
<
MenuList
>
<
MenuList
>
...
@@ -194,74 +173,55 @@ const DataCard = ({ kbId }: { kbId: string }) => {
...
@@ -194,74 +173,55 @@ const DataCard = ({ kbId }: { kbId: string }) => {
<
MenuItem
onClick=
{
onOpenSelectCsvModal
}
>
csv 问答对导入
</
MenuItem
>
<
MenuItem
onClick=
{
onOpenSelectCsvModal
}
>
csv 问答对导入
</
MenuItem
>
</
MenuList
>
</
MenuList
>
</
Menu
>
</
Menu
>
</
Box
>
</
Flex
>
</
Flex
>
<
Flex
m
t
=
{
4
}
>
<
Flex
m
y
=
{
4
}
>
{
(
qaListLen
>
0
||
vectorListLen
>
0
)
&&
(
{
qaListLen
>
0
||
vectorListLen
>
0
?
(
<
Box
fontSize=
{
'xs'
}
>
<
Box
fontSize=
{
'xs'
}
>
{
qaListLen
>
0
?
`${qaListLen}条数据正在拆分,`
:
''
}
{
qaListLen
>
0
?
`${qaListLen}条数据正在拆分,`
:
''
}
{
vectorListLen
>
0
?
`${vectorListLen}条数据正在生成索引,`
:
''
}
{
vectorListLen
>
0
?
`${vectorListLen}条数据正在生成索引,`
:
''
}
请耐心等待...
请耐心等待...
</
Box
>
</
Box
>
)
:
(
<
Box
fontSize=
{
'xs'
}
>
所有数据已就绪~
</
Box
>
)
}
)
}
<
Box
flex=
{
1
}
/>
<
Box
flex=
{
1
}
mr=
{
1
}
/>
<
Input
<
Input
maxW=
{
[
'
9
0%'
,
'300px'
]
}
maxW=
{
[
'
6
0%'
,
'300px'
]
}
size=
{
'sm'
}
size=
{
'sm'
}
value=
{
searchText
}
value=
{
searchText
}
placeholder=
"搜索匹配知识,补充知识和来源,回车确认"
placeholder=
"根据匹配知识,补充知识和来源搜索"
onChange=
{
(
e
)
=>
setSearchText
(
e
.
target
.
value
)
}
onChange=
{
(
e
)
=>
{
setSearchText
(
e
.
target
.
value
);
getFirstData
();
}
}
onBlur=
{
()
=>
{
onBlur=
{
()
=>
{
if
(
searchText
===
lastSearch
.
current
)
return
;
if
(
searchText
===
lastSearch
.
current
)
return
;
getData
(
1
);
getFirstData
();
lastSearch
.
current
=
searchText
;
}
}
}
}
onKeyDown=
{
(
e
)
=>
{
onKeyDown=
{
(
e
)
=>
{
if
(
searchText
===
lastSearch
.
current
)
return
;
if
(
searchText
===
lastSearch
.
current
)
return
;
if
(
e
.
key
===
'Enter'
)
{
if
(
e
.
key
===
'Enter'
)
{
getData
(
1
);
getFirstData
();
lastSearch
.
current
=
searchText
;
}
}
}
}
}
}
/>
/>
</
Flex
>
</
Flex
>
<
TableContainer
mt=
{
4
}
minH=
{
'200px'
}
>
<
Grid
<
Table
>
minH=
{
'200px'
}
<
Thead
>
gridTemplateColumns=
{
[
'1fr'
,
'repeat(2,1fr)'
,
'repeat(3,1fr)'
,
'repeat(4,1fr)'
]
}
<
Tr
>
gridGap=
{
4
}
<
Th
>
匹配的知识点
<
Tooltip
label=
{
'对话时,会将用户的问题和知识库的 "匹配知识点" 进行比较,找到最相似的前 n 条记录,将这些记录的 "匹配知识点"+"补充知识点" 作为 chatgpt 的系统提示词。'
}
>
>
<
QuestionOutlineIcon
ml=
{
1
}
/>
</
Tooltip
>
</
Th
>
<
Th
>
补充知识
</
Th
>
<
Th
>
来源
</
Th
>
<
Th
>
操作
</
Th
>
</
Tr
>
</
Thead
>
<
Tbody
>
{
kbDataList
.
map
((
item
)
=>
(
{
kbDataList
.
map
((
item
)
=>
(
<
Tr
key=
{
item
.
id
}
fontSize=
{
'sm'
}
>
<
Card
<
Td
>
key=
{
item
.
id
}
<
Box
{
...
tdStyles
.
current
}
>
{
item
.
q
}
</
Box
>
cursor=
{
'pointer'
}
</
Td
>
pb=
{
3
}
<
Td
>
userSelect=
{
'none'
}
<
Box
{
...
tdStyles
.
current
}
>
{
item
.
a
||
'-'
}
</
Box
>
boxShadow=
{
'none'
}
</
Td
>
_hover=
{
{
boxShadow
:
'lg'
,
'& .delete'
:
{
display
:
'block'
}
}
}
<
Td
maxW=
{
'15%'
}
whiteSpace=
{
'pre-wrap'
}
userSelect=
{
'all'
}
>
border=
{
'1px solid '
}
{
item
.
source
?.
trim
()
||
'-'
}
borderColor=
{
'myGray.200'
}
</
Td
>
<
Td
>
<
IconButton
mr=
{
5
}
icon=
{
<
EditIcon
/>
}
variant=
{
'base'
}
aria
-
label=
{
'delete'
}
size=
{
'sm'
}
onClick=
{
()
=>
onClick=
{
()
=>
setEditInputData
({
setEditInputData
({
dataId
:
item
.
id
,
dataId
:
item
.
id
,
...
@@ -269,29 +229,47 @@ const DataCard = ({ kbId }: { kbId: string }) => {
...
@@ -269,29 +229,47 @@ const DataCard = ({ kbId }: { kbId: string }) => {
a
:
item
.
a
a
:
item
.
a
})
})
}
}
/>
>
<
Flex
py=
{
3
}
px=
{
4
}
h=
{
'36px'
}
>
<
Box
className=
{
'textEllipsis'
}
flex=
{
1
}
fontSize=
{
'sm'
}
>
{
item
.
source
?.
trim
()
}
</
Box
>
<
IconButton
<
IconButton
className=
"delete"
display=
{
[
'block'
,
'none'
]
}
icon=
{
<
DeleteIcon
/>
}
icon=
{
<
DeleteIcon
/>
}
variant=
{
'base'
}
variant=
{
'base'
}
colorScheme=
{
'gray'
}
colorScheme=
{
'gray'
}
aria
-
label=
{
'delete'
}
aria
-
label=
{
'delete'
}
size=
{
'sm'
}
size=
{
'xs'
}
onClick=
{
async
()
=>
{
borderRadius=
{
'md'
}
lineHeight=
{
1
}
_hover=
{
{
color
:
'red.600'
}
}
onClick=
{
async
(
e
)
=>
{
e
.
stopPropagation
();
await
delOneKbDataByDataId
(
item
.
id
);
await
delOneKbDataByDataId
(
item
.
id
);
refetchData
(
pageNum
);
refetchData
(
pageNum
);
}
}
}
}
/>
/>
</
Td
>
</
Flex
>
</
Tr
>
<
Box
h=
{
'100px'
}
overflow=
{
'hidden'
}
wordBreak=
{
'break-all'
}
px=
{
3
}
color=
{
'myGray.600'
}
>
<
Box
color=
{
'myGray.1000'
}
>
{
item
.
q
}
</
Box
>
<
Box
color=
{
'myGray.600'
}
>
{
item
.
a
}
</
Box
>
</
Box
>
</
Card
>
))
}
))
}
</
Tbody
>
</
Grid
>
</
Table
>
</
TableContainer
>
<
Flex
mt=
{
2
}
justifyContent=
{
'center'
}
>
<
Flex
mt=
{
2
}
justifyContent=
{
'flex-end'
}
>
<
Pagination
/>
<
Pagination
/>
</
Flex
>
</
Flex
>
<
Loading
loading=
{
isLoading
}
fixed=
{
false
}
/>
{
editInputData
!==
undefined
&&
(
{
editInputData
!==
undefined
&&
(
<
InputModal
<
InputModal
kbId=
{
kbId
}
kbId=
{
kbId
}
...
...
client/src/pages/kb/components/Detail.tsx
View file @
6ac7119e
import
React
,
{
useRef
}
from
'react'
;
import
React
,
{
useRef
,
useState
}
from
'react'
;
import
{
useRouter
}
from
'next/router'
;
import
{
useRouter
}
from
'next/router'
;
import
{
Card
,
Box
}
from
'@chakra-ui/react'
;
import
{
Box
}
from
'@chakra-ui/react'
;
import
{
useToast
}
from
'@/hooks/useToast'
;
import
{
useToast
}
from
'@/hooks/useToast'
;
import
{
useForm
}
from
'react-hook-form'
;
import
{
useForm
}
from
'react-hook-form'
;
import
{
useQuery
}
from
'@tanstack/react-query'
;
import
{
useQuery
}
from
'@tanstack/react-query'
;
import
{
useUserStore
}
from
'@/store/user'
;
import
{
useUserStore
}
from
'@/store/user'
;
import
{
KbItemType
}
from
'@/types/plugin'
;
import
{
KbItemType
}
from
'@/types/plugin'
;
import
{
useScreen
}
from
'@/hooks/useScreen'
;
import
DataCard
from
'./DataCard'
;
import
DataCard
from
'./DataCard'
;
import
{
getErrText
}
from
'@/utils/tools'
;
import
{
getErrText
}
from
'@/utils/tools'
;
import
Info
,
{
type
ComponentRef
}
from
'./Info'
;
import
Info
,
{
type
ComponentRef
}
from
'./Info'
;
import
Tabs
from
'@/components/Tabs'
;
enum
TabEnum
{
data
=
'data'
,
test
=
'test'
,
info
=
'info'
}
const
Detail
=
({
kbId
}:
{
kbId
:
string
})
=>
{
const
Detail
=
({
kbId
}:
{
kbId
:
string
})
=>
{
const
{
toast
}
=
useToast
();
const
{
toast
}
=
useToast
();
const
router
=
useRouter
();
const
router
=
useRouter
();
const
{
isPc
}
=
useScreen
();
const
BasicInfo
=
useRef
<
ComponentRef
>
(
null
);
const
BasicInfo
=
useRef
<
ComponentRef
>
(
null
);
const
{
setLastKbId
,
kbDetail
,
getKbDetail
,
loadKbList
,
myKbList
}
=
useUserStore
();
const
{
setLastKbId
,
kbDetail
,
getKbDetail
,
loadKbList
,
myKbList
}
=
useUserStore
();
const
[
currentTab
,
setCurrentTab
]
=
useState
(
TabEnum
.
data
);
const
form
=
useForm
<
KbItemType
>
({
const
form
=
useForm
<
KbItemType
>
({
defaultValues
:
kbDetail
defaultValues
:
kbDetail
...
@@ -42,13 +51,23 @@ const Detail = ({ kbId }: { kbId: string }) => {
...
@@ -42,13 +51,23 @@ const Detail = ({ kbId }: { kbId: string }) => {
});
});
return
(
return
(
<
Box
h=
{
'100%'
}
p=
{
5
}
overflow=
{
'overlay'
}
position=
{
'relative'
}
>
<
Box
bg=
{
'#fcfcfc'
}
h=
{
'100%'
}
p=
{
5
}
overflow=
{
'overlay'
}
position=
{
'relative'
}
>
<
Card
p=
{
6
}
>
<
Box
mb=
{
5
}
>
<
Info
ref=
{
BasicInfo
}
kbId=
{
kbId
}
form=
{
form
}
/>
<
Tabs
</
Card
>
m=
{
'auto'
}
<
Card
p=
{
6
}
mt=
{
5
}
>
w=
{
'260px'
}
<
DataCard
kbId=
{
kbId
}
/>
size=
{
isPc
?
'md'
:
'sm'
}
</
Card
>
list=
{
[
{
id
:
TabEnum
.
data
,
label
:
'数据管理'
},
{
id
:
TabEnum
.
test
,
label
:
'搜索测试'
},
{
id
:
TabEnum
.
info
,
label
:
'基本信息'
}
]
}
activeId=
{
currentTab
}
onChange=
{
(
e
:
any
)
=>
setCurrentTab
(
e
)
}
/>
</
Box
>
{
currentTab
===
TabEnum
.
data
&&
<
DataCard
kbId=
{
kbId
}
/>
}
{
currentTab
===
TabEnum
.
info
&&
<
Info
ref=
{
BasicInfo
}
kbId=
{
kbId
}
form=
{
form
}
/>
}
</
Box
>
</
Box
>
);
);
};
};
...
...
client/src/pages/kb/components/Info.tsx
View file @
6ac7119e
...
@@ -140,88 +140,89 @@ const Info = (
...
@@ -140,88 +140,89 @@ const Info = (
}));
}));
return (
return (
<Box>
<Flex flexDirection={'column'} alignItems={'center'}>
<Flex>
<Flex mt={5} w={'100%'} maxW={'350px'} alignItems={'center'}>
<Box fontWeight={'bold'} fontSize={'2xl'} flex={1}>
<Box flex={'0 0 90px'} w={0}>
知识库信息
知识库头像
</Box>
{kbDetail._id && (
<>
<Button
isLoading={btnLoading}
mr={3}
onClick={handleSubmit(saveSubmitSuccess, saveSubmitError)}
>
保存
</Button>
<IconButton
isLoading={btnLoading}
icon={<DeleteIcon />}
aria-label={''}
variant={'solid'}
colorScheme={'red'}
onClick={openConfirm(onclickDelKb)}
/>
</>
)}
</Flex>
<Flex mt={5} alignItems={'center'}>
<Box flex={'0 0 60px'} w={0}>
头像
</Box>
</Box>
<Box flex={1}>
<Avatar
<Avatar
m={'auto'}
src={getValues('avatar')}
src={getValues('avatar')}
w={['28px', '36
px']}
w={['32px', '40
px']}
h={['28px', '36
px']}
h={['32px', '40
px']}
cursor={'pointer'}
cursor={'pointer'}
title={'点击切换头像'}
title={'点击切换头像'}
onClick={onOpenSelectFile}
onClick={onOpenSelectFile}
/>
/>
</Box>
</Flex>
</Flex>
<FormControl mt={5}>
<FormControl mt={8} w={'100%'} maxW={'350px'} display={'flex'} alignItems={'center'}>
<Flex alignItems={'center'} maxW={'350px'}>
<Box flex={'0 0 90px'} w={0}>
<Box flex={'0 0 60px'} w={0}>
知识库名称
名称
</Box>
</Box>
<Input
<Input
flex={1}
{...register('name', {
{...register('name', {
required: '知识库名称不能为空'
required: '知识库名称不能为空'
})}
})}
/>
/>
</Flex>
</FormControl>
</FormControl>
<Box>
<Flex mt={8} alignItems={'center'} w={'100%'} maxW={'350px'} flexWrap={'wrap'}>
<Flex mt={5} alignItems={'center'} maxW={'350px'} flexWrap={'wrap'}>
<Box flex={'0 0 90px'} w={0}>
<Box flex={'0 0 60px'} w={0}>
分类标签
标签
<Tooltip label={'用空格隔开多个标签,便于搜索'}>
<Tooltip label={'仅用于记忆,用空格隔开多个标签'}>
<QuestionOutlineIcon ml={1} />
<QuestionOutlineIcon ml={1} />
</Tooltip>
</Tooltip>
</Box>
</Box>
<Input
<Input
flex={1}
flex={1}
maxW={'300px'}
ref={InputRef}
ref={InputRef}
placeholder={'标签,使用空格分割。'}
placeholder={'标签,使用空格分割。'}
maxLength={30}
onChange={(e) => {
onChange={(e) => {
setValue('tags', e.target.value);
setValue('tags', e.target.value);
setRefresh(!refresh);
setRefresh(!refresh);
}}
}}
/>
/>
<Box pl={'60px'}
mt={2} w="100%">
<Box
mt={2} w="100%">
{getValues('tags')
{getValues('tags')
.split(' ')
.split(' ')
.filter((item) => item)
.filter((item) => item)
.map((item, i) => (
.map((item, i) => (
<Tag mr={2} mb={2} key={i
}>
<Tag mr={2} mb={2} key={i} whiteSpace={'nowrap'
}>
{item}
{item}
</Tag>
</Tag>
))}
))}
</Box>
</Box>
</Flex>
</Flex>
</Box>
{kbDetail._id && (
<Flex mt={5} w={'100%'} maxW={'350px'} alignItems={'flex-end'}>
<Box flex={'0 0 90px'} w={0}></Box>
<Button
isLoading={btnLoading}
mr={4}
w={'100px'}
onClick={handleSubmit(saveSubmitSuccess, saveSubmitError)}
>
保存
</Button>
<IconButton
isLoading={btnLoading}
icon={<DeleteIcon />}
aria-label={''}
variant={'outline'}
size={'sm'}
colorScheme={'red'}
color={'red.500'}
onClick={openConfirm(onclickDelKb)}
/>
</Flex>
)}
<File onSelect={onSelectFile} />
<File onSelect={onSelectFile} />
<ConfirmChild />
<ConfirmChild />
</
Bo
x>
</
Fle
x>
);
);
};
};
...
...
client/src/pages/kb/components/KbList.tsx
View file @
6ac7119e
...
@@ -119,12 +119,12 @@ const KbList = ({ kbId }: { kbId: string }) => {
...
@@ -119,12 +119,12 @@ const KbList = ({ kbId }: { kbId: string }) => {
{
item
.
name
}
{
item
.
name
}
</
Box
>
</
Box
>
{
/* tags */
}
{
/* tags */
}
<
Box
c
lassName=
"textEllipsis"
color=
{
'myGray.400'
}
py=
{
1
}
>
<
Box
c
olor=
{
'myGray.400'
}
py=
{
1
}
fontSize=
{
'sm'
}
>
{
!
item
.
tags
?
(
{
!
item
.
tags
?
(
<>
{
item
.
tags
||
'你还没设置标签~'
}
</>
<>
{
item
.
tags
||
'你还没设置标签~'
}
</>
)
:
(
)
:
(
item
.
tags
.
split
(
' '
).
map
((
item
,
i
)
=>
(
item
.
tags
.
split
(
' '
).
map
((
item
,
i
)
=>
(
<
Tag
key=
{
i
}
mr=
{
2
}
mb=
{
2
}
>
<
Tag
key=
{
i
}
mr=
{
1
}
mb=
{
1
}
>
{
item
}
{
item
}
</
Tag
>
</
Tag
>
))
))
...
...
client/src/pages/number/index.tsx
View file @
6ac7119e
...
@@ -245,13 +245,14 @@ const NumberSetting = ({ tableType }: { tableType: `${TableEnum}` }) => {
...
@@ -245,13 +245,14 @@ const NumberSetting = ({ tableType }: { tableType: `${TableEnum}` }) => {
<
Card
mt=
{
4
}
px=
{
[
3
,
6
]
}
py=
{
4
}
>
<
Card
mt=
{
4
}
px=
{
[
3
,
6
]
}
py=
{
4
}
>
<
Tabs
<
Tabs
m=
{
'auto'
}
w=
{
'200px'
}
w=
{
'200px'
}
list=
{
tableList
.
current
}
list=
{
tableList
.
current
}
activeId=
{
currentTab
}
activeId=
{
currentTab
}
size=
{
'sm'
}
size=
{
'sm'
}
onChange=
{
(
id
:
any
)
=>
setCurrentTab
(
id
)
}
onChange=
{
(
id
:
any
)
=>
setCurrentTab
(
id
)
}
/>
/>
<
Box
>
<
Box
minH=
{
'300px'
}
>
{
(()
=>
{
{
(()
=>
{
const
item
=
tableList
.
current
.
find
((
item
)
=>
item
.
id
===
currentTab
);
const
item
=
tableList
.
current
.
find
((
item
)
=>
item
.
id
===
currentTab
);
...
...
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