4.6.7 first pr (#726)
This commit is contained in:
@@ -1,44 +1,38 @@
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import { Box, Flex, Button, Textarea, BoxProps, Image, useTheme, Grid } from '@chakra-ui/react';
|
||||
import { Box, Flex, Button, Textarea, useTheme, Grid } from '@chakra-ui/react';
|
||||
import { useFieldArray, useForm } from 'react-hook-form';
|
||||
import {
|
||||
postInsertData2Dataset,
|
||||
putDatasetDataById,
|
||||
delOneDatasetDataById,
|
||||
getDatasetCollectionById
|
||||
getDatasetCollectionById,
|
||||
getDatasetDataItemById
|
||||
} from '@/web/core/dataset/api';
|
||||
import { useToast } from '@/web/common/hooks/useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||
import MyModal from '@/components/MyModal';
|
||||
import MyTooltip from '@/components/MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { getFileAndOpen } from '@/web/core/dataset/utils';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useRequest } from '@/web/common/hooks/useRequest';
|
||||
import { countPromptTokens } from '@fastgpt/global/common/string/tiktoken';
|
||||
import { useConfirm } from '@/web/common/hooks/useConfirm';
|
||||
import { getDefaultIndex, getSourceNameIcon } from '@fastgpt/global/core/dataset/utils';
|
||||
import { feConfigs, vectorModelList } from '@/web/common/system/staticData';
|
||||
import { getDefaultIndex } from '@fastgpt/global/core/dataset/utils';
|
||||
import { vectorModelList } from '@/web/common/system/staticData';
|
||||
import { DatasetDataIndexTypeEnum } from '@fastgpt/global/core/dataset/constant';
|
||||
import { DatasetDataIndexItemType } from '@fastgpt/global/core/dataset/type';
|
||||
import SideTabs from '@/components/SideTabs';
|
||||
import { useLoading } from '@/web/common/hooks/useLoading';
|
||||
import DeleteIcon from '@fastgpt/web/components/common/Icon/delete';
|
||||
import { defaultCollectionDetail } from '@/constants/dataset';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import RawSourceBox from '@/components/core/dataset/RawSourceBox';
|
||||
import MyBox from '@/components/common/MyBox';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
export type RawSourceTextProps = BoxProps & {
|
||||
sourceName?: string;
|
||||
sourceId?: string;
|
||||
canView?: boolean;
|
||||
};
|
||||
export type InputDataType = {
|
||||
id?: string;
|
||||
q: string;
|
||||
a?: string;
|
||||
a: string;
|
||||
indexes: (Omit<DatasetDataIndexItemType, 'dataId'> & {
|
||||
dataId?: string; // pg data id
|
||||
})[];
|
||||
@@ -53,26 +47,25 @@ enum TabEnum {
|
||||
|
||||
const InputDataModal = ({
|
||||
collectionId,
|
||||
dataId,
|
||||
defaultValue,
|
||||
onClose,
|
||||
onSuccess,
|
||||
onDelete
|
||||
}: {
|
||||
collectionId: string;
|
||||
defaultValue: InputDataType;
|
||||
dataId?: string;
|
||||
defaultValue?: { q: string; a?: string };
|
||||
onClose: () => void;
|
||||
onSuccess: (data: InputDataType) => void;
|
||||
onSuccess: (data: InputDataType & { dataId: string }) => void;
|
||||
onDelete?: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { toast } = useToast();
|
||||
const { Loading } = useLoading();
|
||||
const [currentTab, setCurrentTab] = useState(TabEnum.content);
|
||||
|
||||
const { register, handleSubmit, reset, control } = useForm<InputDataType>({
|
||||
defaultValues: defaultValue
|
||||
});
|
||||
const { register, handleSubmit, reset, control } = useForm<InputDataType>();
|
||||
const {
|
||||
fields: indexes,
|
||||
append: appendIndexes,
|
||||
@@ -89,14 +82,15 @@ const InputDataModal = ({
|
||||
id: TabEnum.index,
|
||||
icon: 'kbTest'
|
||||
},
|
||||
...(defaultValue.id
|
||||
...(dataId
|
||||
? [{ label: t('dataset.data.edit.Delete'), id: TabEnum.delete, icon: 'delete' }]
|
||||
: []),
|
||||
{ label: t('dataset.data.edit.Course'), id: TabEnum.doc, icon: 'common/courseLight' }
|
||||
];
|
||||
|
||||
const { ConfirmModal, openConfirm } = useConfirm({
|
||||
content: t('dataset.data.Delete Tip')
|
||||
content: t('dataset.data.Delete Tip'),
|
||||
type: 'delete'
|
||||
});
|
||||
|
||||
const { data: collection = defaultCollectionDetail } = useQuery(
|
||||
@@ -105,6 +99,37 @@ const InputDataModal = ({
|
||||
return getDatasetCollectionById(collectionId);
|
||||
}
|
||||
);
|
||||
const { isFetching: isFetchingData } = useQuery(
|
||||
['getDatasetDataItemById', dataId],
|
||||
() => {
|
||||
if (dataId) return getDatasetDataItemById(dataId);
|
||||
return null;
|
||||
},
|
||||
{
|
||||
onSuccess(res) {
|
||||
if (res) {
|
||||
reset({
|
||||
q: res.q,
|
||||
a: res.a,
|
||||
indexes: res.indexes
|
||||
});
|
||||
} else if (defaultValue) {
|
||||
reset({
|
||||
q: defaultValue.q,
|
||||
a: defaultValue.a,
|
||||
indexes: [getDefaultIndex({ dataId: `${Date.now()}` })]
|
||||
});
|
||||
}
|
||||
},
|
||||
onError(err) {
|
||||
toast({
|
||||
status: 'error',
|
||||
title: getErrText(err)
|
||||
});
|
||||
onClose();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const maxToken = useMemo(() => {
|
||||
const vectorModel =
|
||||
@@ -130,7 +155,7 @@ const InputDataModal = ({
|
||||
|
||||
const data = { ...e };
|
||||
|
||||
data.id = await postInsertData2Dataset({
|
||||
const dataId = await postInsertData2Dataset({
|
||||
collectionId: collection._id,
|
||||
q: e.q,
|
||||
a: e.a,
|
||||
@@ -140,7 +165,10 @@ const InputDataModal = ({
|
||||
)
|
||||
});
|
||||
|
||||
return data;
|
||||
return {
|
||||
...data,
|
||||
dataId
|
||||
};
|
||||
},
|
||||
successToast: t('dataset.data.Input Success Tip'),
|
||||
onSuccess(e) {
|
||||
@@ -158,17 +186,18 @@ const InputDataModal = ({
|
||||
// update
|
||||
const { mutate: onUpdateData, isLoading: isUpdating } = useRequest({
|
||||
mutationFn: async (e: InputDataType) => {
|
||||
if (!e.id) return e;
|
||||
if (!dataId) return e;
|
||||
|
||||
// not exactly same
|
||||
await putDatasetDataById({
|
||||
id: e.id,
|
||||
q: e.q,
|
||||
a: e.a,
|
||||
indexes: e.indexes
|
||||
id: dataId,
|
||||
...e
|
||||
});
|
||||
|
||||
return e;
|
||||
return {
|
||||
dataId,
|
||||
...e
|
||||
};
|
||||
},
|
||||
successToast: t('dataset.data.Update Success Tip'),
|
||||
errorToast: t('common.error.unKnow'),
|
||||
@@ -180,8 +209,8 @@ const InputDataModal = ({
|
||||
// delete
|
||||
const { mutate: onDeleteData, isLoading: isDeleting } = useRequest({
|
||||
mutationFn: () => {
|
||||
if (!onDelete || !defaultValue.id) return Promise.resolve(null);
|
||||
return delOneDatasetDataById(defaultValue.id);
|
||||
if (!onDelete || !dataId) return Promise.resolve(null);
|
||||
return delOneDatasetDataById(dataId);
|
||||
},
|
||||
onSuccess() {
|
||||
if (!onDelete) return;
|
||||
@@ -192,13 +221,16 @@ const InputDataModal = ({
|
||||
errorToast: t('common.error.unKnow')
|
||||
});
|
||||
|
||||
const loading = useMemo(() => isImporting || isUpdating, [isImporting, isUpdating]);
|
||||
const isLoading = useMemo(
|
||||
() => isImporting || isUpdating || isFetchingData || isDeleting,
|
||||
[isImporting, isUpdating, isFetchingData, isDeleting]
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal isOpen={true} isCentered w={'90vw'} maxW={'1440px'} h={'90vh'}>
|
||||
<Flex h={'100%'}>
|
||||
<MyBox isLoading={isLoading} display={'flex'} h={'100%'}>
|
||||
<Box p={5} borderRight={theme.borders.base}>
|
||||
<RawSourceText
|
||||
<RawSourceBox
|
||||
w={'200px'}
|
||||
className="textEllipsis3"
|
||||
whiteSpace={'pre-wrap'}
|
||||
@@ -224,7 +256,7 @@ const InputDataModal = ({
|
||||
<Flex flexDirection={'column'} py={3} flex={1} h={'100%'}>
|
||||
<Box fontSize={'lg'} px={5} fontWeight={'bold'} mb={4}>
|
||||
{currentTab === TabEnum.content && (
|
||||
<>{defaultValue.id ? t('dataset.data.Update Data') : t('dataset.data.Input Data')}</>
|
||||
<>{dataId ? t('dataset.data.Update Data') : t('dataset.data.Input Data')}</>
|
||||
)}
|
||||
{currentTab === TabEnum.index && <> {t('dataset.data.Index Edit')}</>}
|
||||
</Box>
|
||||
@@ -351,82 +383,24 @@ const InputDataModal = ({
|
||||
)}
|
||||
</Box>
|
||||
<Flex justifyContent={'flex-end'} px={5} mt={4}>
|
||||
<Button variant={'whitePrimary'} mr={3} isLoading={loading} onClick={onClose}>
|
||||
<Button variant={'whiteBase'} mr={3} onClick={onClose}>
|
||||
{t('common.Close')}
|
||||
</Button>
|
||||
<MyTooltip label={collection.canWrite ? '' : t('dataset.data.Can not edit')}>
|
||||
<Button
|
||||
isDisabled={!collection.canWrite}
|
||||
isLoading={loading}
|
||||
// @ts-ignore
|
||||
onClick={handleSubmit(defaultValue.id ? onUpdateData : sureImportData)}
|
||||
onClick={handleSubmit(dataId ? onUpdateData : sureImportData)}
|
||||
>
|
||||
{defaultValue.id ? t('common.Confirm Update') : t('common.Confirm Import')}
|
||||
{dataId ? t('common.Confirm Update') : t('common.Confirm Import')}
|
||||
</Button>
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</MyBox>
|
||||
<ConfirmModal />
|
||||
<Loading fixed={false} loading={isDeleting} />
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputDataModal;
|
||||
|
||||
export function RawSourceText({
|
||||
sourceId,
|
||||
sourceName = '',
|
||||
canView = true,
|
||||
...props
|
||||
}: RawSourceTextProps) {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { setLoading } = useSystemStore();
|
||||
|
||||
const canPreview = useMemo(() => !!sourceId && canView, [canView, sourceId]);
|
||||
|
||||
const icon = useMemo(() => getSourceNameIcon({ sourceId, sourceName }), [sourceId, sourceName]);
|
||||
|
||||
return (
|
||||
<MyTooltip
|
||||
label={canPreview ? t('file.Click to view file') || '' : ''}
|
||||
shouldWrapChildren={false}
|
||||
>
|
||||
<Box
|
||||
color={'myGray.600'}
|
||||
display={'inline-flex'}
|
||||
whiteSpace={'nowrap'}
|
||||
{...(canPreview
|
||||
? {
|
||||
cursor: 'pointer',
|
||||
textDecoration: 'underline',
|
||||
onClick: async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await getFileAndOpen(sourceId as string);
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: t(getErrText(error, 'error.fileNotFound')),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
: {})}
|
||||
{...props}
|
||||
>
|
||||
<Image src={icon} alt="" w={['14px', '16px']} mr={2} />
|
||||
<Box
|
||||
maxW={['200px', '300px']}
|
||||
className={props.className ?? 'textEllipsis'}
|
||||
wordBreak={'break-all'}
|
||||
>
|
||||
{sourceName || t('common.UnKnow Source')}
|
||||
</Box>
|
||||
</Box>
|
||||
</MyTooltip>
|
||||
);
|
||||
}
|
||||
export default React.memo(InputDataModal);
|
||||
|
||||
Reference in New Issue
Block a user