This commit is contained in:
Archer
2023-10-22 23:54:04 +08:00
committed by GitHub
parent 3091a90df6
commit a3534407bf
365 changed files with 7266 additions and 6055 deletions

View File

@@ -0,0 +1,167 @@
import React, { useCallback, useState, useRef } from 'react';
import { Box, Flex, Button, ModalHeader, ModalFooter, ModalBody, Input } from '@chakra-ui/react';
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
import { useForm } from 'react-hook-form';
import { compressImg } from '@/web/common/file/utils';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { useToast } from '@/web/common/hooks/useToast';
import { useRouter } from 'next/router';
import { useSystemStore } from '@/web/common/system/useSystemStore';
import { useRequest } from '@/web/common/hooks/useRequest';
import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
import MyModal from '@/components/MyModal';
import { postCreateDataset } from '@/web/core/dataset/api';
import type { CreateDatasetParams } from '@/global/core/api/datasetReq.d';
import MySelect from '@/components/Select';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import { vectorModelList } from '@/web/common/system/staticData';
import Tag from '@/components/Tag';
const CreateModal = ({ onClose, parentId }: { onClose: () => void; parentId?: string }) => {
const [refresh, setRefresh] = useState(false);
const { toast } = useToast();
const router = useRouter();
const { isPc } = useSystemStore();
const { register, setValue, getValues, handleSubmit } = useForm<CreateDatasetParams>({
defaultValues: {
avatar: '/icon/logo.svg',
name: '',
tags: [],
vectorModel: vectorModelList[0].model,
type: 'dataset',
parentId
}
});
const InputRef = useRef<HTMLInputElement>(null);
const { File, onOpen: onOpenSelectFile } = useSelectFile({
fileType: '.jpg,.png',
multiple: false
});
const onSelectFile = useCallback(
async (e: File[]) => {
const file = e[0];
if (!file) return;
try {
const src = await compressImg({
file,
maxW: 100,
maxH: 100
});
setValue('avatar', src);
setRefresh((state) => !state);
} catch (err: any) {
toast({
title: getErrText(err, '头像选择异常'),
status: 'warning'
});
}
},
[setValue, toast]
);
/* create a new kb and router to it */
const { mutate: onclickCreate, isLoading: creating } = useRequest({
mutationFn: async (data: CreateDatasetParams) => {
const id = await postCreateDataset(data);
return id;
},
successToast: '创建成功',
errorToast: '创建知识库出现意外',
onSuccess(id) {
router.push(`/dataset/detail?datasetId=${id}`);
}
});
return (
<MyModal isOpen onClose={onClose} isCentered={!isPc} w={'400px'}>
<ModalHeader fontSize={'2xl'}></ModalHeader>
<ModalBody>
<Box color={'myGray.800'} fontWeight={'bold'}>
</Box>
<Flex mt={3} alignItems={'center'}>
<MyTooltip label={'点击设置头像'}>
<Avatar
flexShrink={0}
src={getValues('avatar')}
w={['28px', '32px']}
h={['28px', '32px']}
cursor={'pointer'}
borderRadius={'md'}
onClick={onOpenSelectFile}
/>
</MyTooltip>
<Input
ml={3}
flex={1}
autoFocus
bg={'myWhite.600'}
{...register('name', {
required: '知识库名称不能为空~'
})}
/>
</Flex>
<Flex mt={6} alignItems={'center'}>
<Box flex={'0 0 80px'}></Box>
<Box flex={1}>
<MySelect
w={'100%'}
value={getValues('vectorModel')}
list={vectorModelList.map((item) => ({
label: item.name,
value: item.model
}))}
onchange={(e) => {
setValue('vectorModel', e);
setRefresh((state) => !state);
}}
/>
</Box>
</Flex>
<Flex mt={6} alignItems={'center'} w={'100%'}>
<Box flex={'0 0 80px'}>
<MyTooltip label={'用空格隔开多个标签,便于搜索'} forceShow>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Box>
<Input
flex={1}
ref={InputRef}
placeholder={'标签,使用空格分割。'}
maxLength={30}
onChange={(e) => {
setValue('tags', e.target.value.split(' '));
setRefresh(!refresh);
}}
/>
</Flex>
<Flex mt={2} flexWrap={'wrap'}>
{getValues('tags')
.filter((item) => item)
.map((item, i) => (
<Tag mr={2} mb={2} key={i} whiteSpace={'nowrap'}>
{item}
</Tag>
))}
</Flex>
</ModalBody>
<ModalFooter>
<Button variant={'base'} mr={3} onClick={onClose}>
</Button>
<Button isLoading={creating} onClick={handleSubmit((data) => onclickCreate(data))}>
</Button>
</ModalFooter>
<File onSelect={onSelectFile} />
</MyModal>
);
};
export default CreateModal;

View File

@@ -0,0 +1,180 @@
import React, { useMemo, useState } from 'react';
import {
Card,
Flex,
Box,
Button,
ModalBody,
ModalHeader,
ModalFooter,
useTheme,
Grid
} from '@chakra-ui/react';
import Avatar from '@/components/Avatar';
import MyTooltip from '@/components/MyTooltip';
import MyModal from '@/components/MyModal';
import MyIcon from '@/components/Icon';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constant';
import { useTranslation } from 'react-i18next';
import { useQuery } from '@tanstack/react-query';
import { getDatasets, putDatasetById, getDatasetPaths } from '@/web/core/dataset/api';
import { useRequest } from '@/web/common/hooks/useRequest';
const MoveModal = ({
onClose,
onSuccess,
moveDataId
}: {
onClose: () => void;
onSuccess: () => void;
moveDataId: string;
}) => {
const { t } = useTranslation();
const theme = useTheme();
const [parentId, setParentId] = useState<string>('');
const { data } = useQuery(['getDatasets', parentId], () => {
return Promise.all([getDatasets({ parentId, type: 'folder' }), getDatasetPaths(parentId)]);
});
const paths = useMemo(
() => [
{
parentId: '',
parentName: t('dataset.My Dataset')
},
...(data?.[1] || [])
],
[data, t]
);
const folderList = useMemo(
() => (data?.[0] || []).filter((item) => item._id !== moveDataId),
[moveDataId, data]
);
const { mutate, isLoading } = useRequest({
mutationFn: () => putDatasetById({ id: moveDataId, parentId }),
onSuccess,
errorToast: t('dataset.Move Failed')
});
return (
<MyModal isOpen={true} maxW={['90vw', '800px']} w={'800px'} onClose={onClose}>
<Flex flexDirection={'column'} h={['90vh', 'auto']}>
<ModalHeader>
{!!parentId ? (
<Flex flex={1} userSelect={'none'} fontSize={['sm', 'lg']} fontWeight={'normal'}>
{paths.map((item, i) => (
<Flex key={item.parentId} mr={2} alignItems={'center'}>
<Box
borderRadius={'md'}
{...(i === paths.length - 1
? {
cursor: 'default'
}
: {
cursor: 'pointer',
_hover: {
color: 'myBlue.600'
},
onClick: () => {
setParentId(item.parentId);
}
})}
>
{item.parentName}
</Box>
{i !== paths.length - 1 && (
<MyIcon name={'rightArrowLight'} color={'myGray.500'} w={['18px', '24px']} />
)}
</Flex>
))}
</Flex>
) : (
<Box></Box>
)}
</ModalHeader>
<ModalBody
flex={['1 0 0', '0 0 auto']}
maxH={'80vh'}
overflowY={'auto'}
display={'grid'}
userSelect={'none'}
>
<Grid
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
gridGap={3}
>
{folderList.map((item) =>
(() => {
return (
<MyTooltip
key={item._id}
label={
item.type === DatasetTypeEnum.dataset
? t('dataset.Select Dataset')
: t('dataset.Select Folder')
}
>
<Card
p={3}
border={theme.borders.base}
boxShadow={'sm'}
h={'80px'}
cursor={'pointer'}
_hover={{
boxShadow: 'md'
}}
onClick={() => {
setParentId(item._id);
}}
>
<Flex alignItems={'center'} h={'38px'}>
<Avatar src={item.avatar} w={['24px', '28px']}></Avatar>
<Box
className="textEllipsis"
ml={3}
fontWeight={'bold'}
fontSize={['md', 'lg', 'xl']}
>
{item.name}
</Box>
</Flex>
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
{item.type === DatasetTypeEnum.folder ? (
<Box color={'myGray.500'}>{t('Folder')}</Box>
) : (
<>
<MyIcon mr={1} name="kbTest" w={'12px'} />
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
</>
)}
</Flex>
</Card>
</MyTooltip>
);
})()
)}
</Grid>
{folderList.length === 0 && (
<Flex mt={5} flexDirection={'column'} alignItems={'center'}>
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
<Box mt={2} color={'myGray.500'}>
{t('common.folder.No Folder')}
</Box>
</Flex>
)}
</ModalBody>
<ModalFooter>
<Button isLoading={isLoading} onClick={mutate}>
{t('dataset.Confirm move the folder')}
</Button>
</ModalFooter>
</Flex>
</MyModal>
);
};
export default MoveModal;