Commit 6ac7119e by archer

feat: kb UI

parent daf1148b
...@@ -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/)
......
...@@ -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={'1px'} py={'2px'}
borderRadius={'md'} borderRadius={'md'}
fontSize={'xs'} fontSize={'xs'}
{...theme} {...theme}
......
...@@ -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%, #d6e8ff 100%)', hoverBlueGradient: 'linear-gradient(to top left, #d6e8ff 0%, #f0f7ff 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%)'
}, },
......
...@@ -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);
......
...@@ -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],
......
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>
); );
}; };
......
...@@ -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>
<Box flex={1}>
<Avatar
m={'auto'}
src={getValues('avatar')}
w={['32px', '40px']}
h={['32px', '40px']}
cursor={'pointer'}
title={'点击切换头像'}
onClick={onOpenSelectFile}
/>
</Box> </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>
<Flex mt={5} alignItems={'center'}> <FormControl mt={8} w={'100%'} maxW={'350px'} display={'flex'} alignItems={'center'}>
<Box flex={'0 0 60px'} w={0}> <Box flex={'0 0 90px'} w={0}>
头像 知识库名称
</Box> </Box>
<Avatar <Input
src={getValues('avatar')} flex={1}
w={['28px', '36px']} {...register('name', {
h={['28px', '36px']} required: '知识库名称不能为空'
cursor={'pointer'} })}
title={'点击切换头像'}
onClick={onOpenSelectFile}
/> />
</Flex>
<FormControl mt={5}>
<Flex alignItems={'center'} maxW={'350px'}>
<Box flex={'0 0 60px'} w={0}>
名称
</Box>
<Input
{...register('name', {
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={'标签,使用空格分割。'}
onChange={(e) => { maxLength={30}
setValue('tags', e.target.value); onChange={(e) => {
setRefresh(!refresh); setValue('tags', e.target.value);
}} setRefresh(!refresh);
}}
/>
<Box mt={2} w="100%">
{getValues('tags')
.split(' ')
.filter((item) => item)
.map((item, i) => (
<Tag mr={2} mb={2} key={i} whiteSpace={'nowrap'}>
{item}
</Tag>
))}
</Box>
</Flex>
{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)}
/> />
<Box pl={'60px'} mt={2} w="100%">
{getValues('tags')
.split(' ')
.filter((item) => item)
.map((item, i) => (
<Tag mr={2} mb={2} key={i}>
{item}
</Tag>
))}
</Box>
</Flex> </Flex>
</Box> )}
<File onSelect={onSelectFile} /> <File onSelect={onSelectFile} />
<ConfirmChild /> <ConfirmChild />
</Box> </Flex>
); );
}; };
......
...@@ -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 className="textEllipsis" color={'myGray.400'} py={1}> <Box color={'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>
)) ))
......
...@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment