mirror of
https://github.com/labring/FastGPT.git
synced 2025-08-03 21:48:02 +00:00
fix: mark modal cannot select folder (#327)
This commit is contained in:
@@ -1,58 +1,43 @@
|
|||||||
import React, { useRef, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import {
|
import { ModalBody, useTheme, ModalFooter, Button, Box, Card, Flex, Grid } from '@chakra-ui/react';
|
||||||
ModalBody,
|
|
||||||
useTheme,
|
|
||||||
ModalFooter,
|
|
||||||
Button,
|
|
||||||
ModalHeader,
|
|
||||||
Box,
|
|
||||||
Card,
|
|
||||||
Flex
|
|
||||||
} from '@chakra-ui/react';
|
|
||||||
import MyModal from '../MyModal';
|
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useQuery } from '@tanstack/react-query';
|
|
||||||
import { useDatasetStore } from '@/store/dataset';
|
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import Avatar from '../Avatar';
|
import Avatar from '../Avatar';
|
||||||
import MyIcon from '@/components/Icon';
|
import MyIcon from '@/components/Icon';
|
||||||
import { useGlobalStore } from '@/store/global';
|
import { KbTypeEnum } from '@/constants/kb';
|
||||||
|
import DatasetSelectModal, { useDatasetSelect } from '@/components/core/dataset/SelectModal';
|
||||||
|
|
||||||
const SelectDataset = ({
|
const SelectDataset = ({
|
||||||
|
isOpen,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
onClose
|
onClose
|
||||||
}: {
|
}: {
|
||||||
|
isOpen: boolean;
|
||||||
onSuccess: (kbId: string) => void;
|
onSuccess: (kbId: string) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { isPc } = useGlobalStore();
|
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { myKbList, loadKbList } = useDatasetStore();
|
|
||||||
const [selectedId, setSelectedId] = useState<string>();
|
const [selectedId, setSelectedId] = useState<string>();
|
||||||
|
const { paths, parentId, setParentId, datasets } = useDatasetSelect();
|
||||||
useQuery(['loadKbList'], () => loadKbList());
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal isOpen={true} onClose={onClose} w={'100%'} maxW={['90vw', '900px']} isCentered={!isPc}>
|
<DatasetSelectModal
|
||||||
<Flex flexDirection={'column'} h={['90vh', 'auto']}>
|
isOpen={isOpen}
|
||||||
<ModalHeader>
|
paths={paths}
|
||||||
<Box>{t('chat.Select Mark Kb')}</Box>
|
onClose={onClose}
|
||||||
<Box fontSize={'sm'} color={'myGray.500'} fontWeight={'normal'}>
|
parentId={parentId}
|
||||||
{t('chat.Select Mark Kb Desc')}
|
setParentId={setParentId}
|
||||||
</Box>
|
tips={t('chat.Select Mark Kb Desc')}
|
||||||
</ModalHeader>
|
>
|
||||||
<ModalBody
|
<ModalBody flex={['1 0 0', '0 0 auto']} maxH={'80vh'} overflowY={'auto'}>
|
||||||
flex={['1 0 0', '0 0 auto']}
|
<Grid
|
||||||
maxH={'80vh'}
|
|
||||||
overflowY={'auto'}
|
|
||||||
display={'grid'}
|
|
||||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
|
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
|
||||||
gridGap={3}
|
gridGap={3}
|
||||||
userSelect={'none'}
|
userSelect={'none'}
|
||||||
>
|
>
|
||||||
{myKbList.map((item) =>
|
{datasets.map((item) =>
|
||||||
(() => {
|
(() => {
|
||||||
const selected = selectedId === item._id;
|
const selected = selectedId === item._id;
|
||||||
return (
|
return (
|
||||||
@@ -72,7 +57,11 @@ const SelectDataset = ({
|
|||||||
}
|
}
|
||||||
: {})}
|
: {})}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedId(item._id);
|
if (item.type === KbTypeEnum.folder) {
|
||||||
|
setParentId(item._id);
|
||||||
|
} else {
|
||||||
|
setSelectedId(item._id);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flex alignItems={'center'} h={'38px'}>
|
<Flex alignItems={'center'} h={'38px'}>
|
||||||
@@ -89,28 +78,36 @@ const SelectDataset = ({
|
|||||||
);
|
);
|
||||||
})()
|
})()
|
||||||
)}
|
)}
|
||||||
</ModalBody>
|
</Grid>
|
||||||
<ModalFooter>
|
{datasets.length === 0 && (
|
||||||
<Button variant={'base'} mr={2} onClick={onClose}>
|
<Flex mt={5} flexDirection={'column'} alignItems={'center'}>
|
||||||
{t('Cancel')}
|
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
||||||
</Button>
|
<Box mt={2} color={'myGray.500'}>
|
||||||
<Button
|
这个目录已经没东西可选了~
|
||||||
onClick={() => {
|
</Box>
|
||||||
if (!selectedId) {
|
</Flex>
|
||||||
return toast({
|
)}
|
||||||
status: 'warning',
|
</ModalBody>
|
||||||
title: t('Select value is empty')
|
<ModalFooter>
|
||||||
});
|
<Button variant={'base'} mr={2} onClick={onClose}>
|
||||||
}
|
{t('Cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
if (!selectedId) {
|
||||||
|
return toast({
|
||||||
|
status: 'warning',
|
||||||
|
title: t('Select value is empty')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
onSuccess(selectedId);
|
onSuccess(selectedId);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('Confirm')}
|
{t('Confirm')}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Flex>
|
</DatasetSelectModal>
|
||||||
</MyModal>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -985,13 +985,12 @@ const ChatBox = (
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{/* select one dataset to insert markData */}
|
{/* select one dataset to insert markData */}
|
||||||
{adminMarkData && !adminMarkData.kbId && (
|
<SelectDataset
|
||||||
<SelectDataset
|
isOpen={!!adminMarkData && !adminMarkData.kbId}
|
||||||
onClose={() => setAdminMarkData(undefined)}
|
onClose={() => setAdminMarkData(undefined)}
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
onSuccess={(kbId) => setAdminMarkData((state) => ({ ...state, kbId }))}
|
onSuccess={(kbId) => setAdminMarkData((state) => ({ ...state, kbId }))}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{/* edit markData modal */}
|
{/* edit markData modal */}
|
||||||
{adminMarkData && adminMarkData.kbId && (
|
{adminMarkData && adminMarkData.kbId && (
|
||||||
<InputDataModal
|
<InputDataModal
|
||||||
|
@@ -101,7 +101,7 @@ const MyIcon = ({ name, w = 'auto', h = 'auto', ...props }: { name: IconName } &
|
|||||||
.catch((error) => console.log(error));
|
.catch((error) => console.log(error));
|
||||||
}, [name]);
|
}, [name]);
|
||||||
|
|
||||||
return name ? (
|
return !!name && !!iconPaths[name] ? (
|
||||||
<Icon
|
<Icon
|
||||||
{...IconComponent}
|
{...IconComponent}
|
||||||
w={w}
|
w={w}
|
||||||
|
122
client/src/components/core/dataset/SelectModal.tsx
Normal file
122
client/src/components/core/dataset/SelectModal.tsx
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
import { getKbList, getKbPaths } from '@/api/plugins/kb';
|
||||||
|
import MyModal from '@/components/MyModal';
|
||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import React, { Dispatch, useMemo, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useGlobalStore } from '@/store/global';
|
||||||
|
import { Box, Flex, ModalHeader } from '@chakra-ui/react';
|
||||||
|
import MyIcon from '@/components/Icon';
|
||||||
|
|
||||||
|
type PathItemType = {
|
||||||
|
parentId: string;
|
||||||
|
parentName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const DatasetSelectModal = ({
|
||||||
|
isOpen,
|
||||||
|
parentId,
|
||||||
|
setParentId,
|
||||||
|
paths,
|
||||||
|
onClose,
|
||||||
|
tips,
|
||||||
|
children
|
||||||
|
}: {
|
||||||
|
isOpen: boolean;
|
||||||
|
parentId?: string;
|
||||||
|
setParentId: Dispatch<string>;
|
||||||
|
paths: PathItemType[];
|
||||||
|
onClose: () => void;
|
||||||
|
tips?: string | null;
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const { isPc } = useGlobalStore();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyModal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
w={'100%'}
|
||||||
|
maxW={['90vw', '900px']}
|
||||||
|
isCentered={!isPc}
|
||||||
|
>
|
||||||
|
<Flex flexDirection={'column'} h={['90vh', 'auto']}>
|
||||||
|
<ModalHeader>
|
||||||
|
{!!parentId ? (
|
||||||
|
<Flex
|
||||||
|
flex={1}
|
||||||
|
userSelect={'none'}
|
||||||
|
fontSize={['sm', 'lg']}
|
||||||
|
fontWeight={'normal'}
|
||||||
|
color={'myGray.900'}
|
||||||
|
>
|
||||||
|
{paths.map((item, i) => (
|
||||||
|
<Flex key={item.parentId} mr={2} alignItems={'center'}>
|
||||||
|
<Box
|
||||||
|
fontSize={'lg'}
|
||||||
|
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>{t('chat.Select Mark Kb')}</Box>
|
||||||
|
)}
|
||||||
|
{!!tips && (
|
||||||
|
<Box fontSize={'sm'} color={'myGray.500'} fontWeight={'normal'}>
|
||||||
|
{tips}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</ModalHeader>
|
||||||
|
{children}
|
||||||
|
</Flex>
|
||||||
|
</MyModal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useDatasetSelect = () => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const [parentId, setParentId] = useState<string>();
|
||||||
|
|
||||||
|
const { data } = useQuery(['loadDatasetData', parentId], () =>
|
||||||
|
Promise.all([getKbList({ parentId }), getKbPaths(parentId)])
|
||||||
|
);
|
||||||
|
|
||||||
|
const paths = useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
parentId: '',
|
||||||
|
parentName: t('kb.My Dataset')
|
||||||
|
},
|
||||||
|
...(data?.[1] || [])
|
||||||
|
],
|
||||||
|
[data, t]
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
parentId,
|
||||||
|
setParentId,
|
||||||
|
datasets: data?.[0] || [],
|
||||||
|
paths
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DatasetSelectModal;
|
@@ -71,7 +71,7 @@ const Settings = ({ appId }: { appId: string }) => {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { appDetail, updateAppDetail } = useUserStore();
|
const { appDetail, updateAppDetail } = useUserStore();
|
||||||
const { loadAllDatasets, datasets } = useDatasetStore();
|
const { loadAllDatasets, allDatasets } = useDatasetStore();
|
||||||
const { isPc } = useGlobalStore();
|
const { isPc } = useGlobalStore();
|
||||||
|
|
||||||
const [editVariable, setEditVariable] = useState<VariableItemType>();
|
const [editVariable, setEditVariable] = useState<VariableItemType>();
|
||||||
@@ -131,8 +131,8 @@ const Settings = ({ appId }: { appId: string }) => {
|
|||||||
);
|
);
|
||||||
}, [getValues, refresh]);
|
}, [getValues, refresh]);
|
||||||
const selectedKbList = useMemo(
|
const selectedKbList = useMemo(
|
||||||
() => datasets.filter((item) => kbList.find((kb) => kb.kbId === item._id)),
|
() => allDatasets.filter((item) => kbList.find((kb) => kb.kbId === item._id)),
|
||||||
[datasets, kbList]
|
[allDatasets, kbList]
|
||||||
);
|
);
|
||||||
|
|
||||||
/* 点击删除 */
|
/* 点击删除 */
|
||||||
@@ -564,16 +564,15 @@ const Settings = ({ appId }: { appId: string }) => {
|
|||||||
defaultData={getValues('chatModel')}
|
defaultData={getValues('chatModel')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isOpenKbSelect && (
|
<KBSelectModal
|
||||||
<KBSelectModal
|
isOpen={isOpenKbSelect}
|
||||||
activeKbs={selectedKbList.map((item) => ({
|
activeKbs={selectedKbList.map((item) => ({
|
||||||
kbId: item._id,
|
kbId: item._id,
|
||||||
vectorModel: item.vectorModel
|
vectorModel: item.vectorModel
|
||||||
}))}
|
}))}
|
||||||
onClose={onCloseKbSelect}
|
onClose={onCloseKbSelect}
|
||||||
onChange={replaceKbList}
|
onChange={replaceKbList}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
{isOpenKbParams && (
|
{isOpenKbParams && (
|
||||||
<KbParamsModal
|
<KbParamsModal
|
||||||
searchEmptyText={getValues('kb.searchEmptyText')}
|
searchEmptyText={getValues('kb.searchEmptyText')}
|
||||||
|
@@ -5,19 +5,16 @@ import {
|
|||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
ModalHeader,
|
|
||||||
ModalFooter,
|
ModalFooter,
|
||||||
useTheme,
|
useTheme,
|
||||||
Textarea,
|
Textarea,
|
||||||
Grid,
|
Grid,
|
||||||
Divider
|
Divider
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { getKbPaths } from '@/api/plugins/kb';
|
|
||||||
import Avatar from '@/components/Avatar';
|
import Avatar from '@/components/Avatar';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||||
import type { SelectedKbType } from '@/types/plugin';
|
import type { SelectedKbType } from '@/types/plugin';
|
||||||
import { useGlobalStore } from '@/store/global';
|
|
||||||
import { useToast } from '@/hooks/useToast';
|
import { useToast } from '@/hooks/useToast';
|
||||||
import MySlider from '@/components/Slider';
|
import MySlider from '@/components/Slider';
|
||||||
import MyTooltip from '@/components/MyTooltip';
|
import MyTooltip from '@/components/MyTooltip';
|
||||||
@@ -28,6 +25,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { useDatasetStore } from '@/store/dataset';
|
import { useDatasetStore } from '@/store/dataset';
|
||||||
import { feConfigs } from '@/store/static';
|
import { feConfigs } from '@/store/static';
|
||||||
|
import DatasetSelectModal, { useDatasetSelect } from '@/components/core/dataset/SelectModal';
|
||||||
|
|
||||||
export type KbParamsType = {
|
export type KbParamsType = {
|
||||||
searchSimilarity: number;
|
searchSimilarity: number;
|
||||||
@@ -36,10 +34,12 @@ export type KbParamsType = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const KBSelectModal = ({
|
export const KBSelectModal = ({
|
||||||
|
isOpen,
|
||||||
activeKbs = [],
|
activeKbs = [],
|
||||||
onChange,
|
onChange,
|
||||||
onClose
|
onClose
|
||||||
}: {
|
}: {
|
||||||
|
isOpen: boolean;
|
||||||
activeKbs: SelectedKbType;
|
activeKbs: SelectedKbType;
|
||||||
onChange: (e: SelectedKbType) => void;
|
onChange: (e: SelectedKbType) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -47,229 +47,156 @@ export const KBSelectModal = ({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const [selectedKbList, setSelectedKbList] = useState<SelectedKbType>(activeKbs);
|
const [selectedKbList, setSelectedKbList] = useState<SelectedKbType>(activeKbs);
|
||||||
const { isPc } = useGlobalStore();
|
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const [parentId, setParentId] = useState<string>();
|
const { paths, parentId, setParentId, datasets } = useDatasetSelect();
|
||||||
const { myKbList, loadKbList, datasets, loadAllDatasets } = useDatasetStore();
|
const { allDatasets, loadAllDatasets } = useDatasetStore();
|
||||||
|
|
||||||
const { data } = useQuery(['loadKbList', parentId], () => {
|
|
||||||
return Promise.all([loadKbList(parentId), getKbPaths(parentId)]);
|
|
||||||
});
|
|
||||||
useQuery(['loadAllDatasets'], loadAllDatasets);
|
useQuery(['loadAllDatasets'], loadAllDatasets);
|
||||||
const paths = useMemo(
|
|
||||||
() => [
|
|
||||||
{
|
|
||||||
parentId: '',
|
|
||||||
parentName: t('kb.My Dataset')
|
|
||||||
},
|
|
||||||
...(data?.[1] || [])
|
|
||||||
],
|
|
||||||
[data, t]
|
|
||||||
);
|
|
||||||
const filterKbList = useMemo(() => {
|
const filterKbList = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
selected: datasets.filter((item) => selectedKbList.find((kb) => kb.kbId === item._id)),
|
selected: allDatasets.filter((item) => selectedKbList.find((kb) => kb.kbId === item._id)),
|
||||||
unSelected: myKbList.filter((item) => !selectedKbList.find((kb) => kb.kbId === item._id))
|
unSelected: datasets.filter((item) => !selectedKbList.find((kb) => kb.kbId === item._id))
|
||||||
};
|
};
|
||||||
}, [myKbList, datasets, selectedKbList]);
|
}, [datasets, allDatasets, selectedKbList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal
|
<DatasetSelectModal
|
||||||
isOpen={true}
|
isOpen={isOpen}
|
||||||
isCentered={!isPc}
|
paths={paths}
|
||||||
maxW={['90vw', '800px']}
|
parentId={parentId}
|
||||||
w={'800px'}
|
setParentId={setParentId}
|
||||||
|
tips={'仅能选择同一个索引模型的知识库'}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
>
|
>
|
||||||
<Flex flexDirection={'column'} h={['90vh', 'auto']}>
|
<ModalBody flex={['1 0 0', '0 0 auto']} maxH={'80vh'} overflowY={'auto'} userSelect={'none'}>
|
||||||
<ModalHeader>
|
<Grid gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']} gridGap={3}>
|
||||||
{!!parentId ? (
|
{filterKbList.selected.map((item) =>
|
||||||
<Flex
|
(() => {
|
||||||
flex={1}
|
return (
|
||||||
userSelect={'none'}
|
<Card
|
||||||
fontSize={['sm', 'lg']}
|
key={item._id}
|
||||||
fontWeight={'normal'}
|
p={3}
|
||||||
color={'myGray.900'}
|
border={theme.borders.base}
|
||||||
>
|
boxShadow={'sm'}
|
||||||
{paths.map((item, i) => (
|
bg={'myBlue.300'}
|
||||||
<Flex key={item.parentId} mr={2} alignItems={'center'}>
|
>
|
||||||
<Box
|
<Flex alignItems={'center'} h={'38px'}>
|
||||||
fontSize={'lg'}
|
<Avatar src={item.avatar} w={['24px', '28px']}></Avatar>
|
||||||
borderRadius={'md'}
|
<Box flex={'1 0 0'} mx={3}>
|
||||||
{...(i === paths.length - 1
|
{item.name}
|
||||||
? {
|
</Box>
|
||||||
cursor: 'default'
|
<MyIcon
|
||||||
}
|
name={'delete'}
|
||||||
: {
|
w={'14px'}
|
||||||
cursor: 'pointer',
|
cursor={'pointer'}
|
||||||
_hover: {
|
_hover={{ color: 'red.500' }}
|
||||||
color: 'myBlue.600'
|
onClick={() => {
|
||||||
},
|
setSelectedKbList((state) => state.filter((kb) => kb.kbId !== item._id));
|
||||||
onClick: () => {
|
}}
|
||||||
setParentId(item.parentId);
|
/>
|
||||||
}
|
</Flex>
|
||||||
})}
|
</Card>
|
||||||
>
|
);
|
||||||
{item.parentName}
|
})()
|
||||||
</Box>
|
|
||||||
{i !== paths.length - 1 && (
|
|
||||||
<MyIcon name={'rightArrowLight'} color={'myGray.500'} w={['18px', '24px']} />
|
|
||||||
)}
|
|
||||||
</Flex>
|
|
||||||
))}
|
|
||||||
</Flex>
|
|
||||||
) : (
|
|
||||||
<Box>关联的知识库({selectedKbList.length})</Box>
|
|
||||||
)}
|
)}
|
||||||
{isPc && (
|
</Grid>
|
||||||
<Box fontSize={'sm'} color={'myGray.500'} fontWeight={'normal'}>
|
|
||||||
仅能选择同一个索引模型的知识库
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
</ModalHeader>
|
|
||||||
|
|
||||||
<ModalBody
|
{filterKbList.selected.length > 0 && <Divider my={3} />}
|
||||||
flex={['1 0 0', '0 0 auto']}
|
|
||||||
maxH={'80vh'}
|
<Grid gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']} gridGap={3}>
|
||||||
overflowY={'auto'}
|
{filterKbList.unSelected.map((item) =>
|
||||||
display={'grid'}
|
(() => {
|
||||||
userSelect={'none'}
|
return (
|
||||||
>
|
<MyTooltip
|
||||||
<Grid
|
key={item._id}
|
||||||
h={'auto'}
|
label={
|
||||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
|
item.type === KbTypeEnum.dataset
|
||||||
gridGap={3}
|
? t('kb.Select Dataset')
|
||||||
>
|
: t('kb.Select Folder')
|
||||||
{filterKbList.selected.map((item) =>
|
}
|
||||||
(() => {
|
>
|
||||||
return (
|
|
||||||
<Card
|
<Card
|
||||||
key={item._id}
|
|
||||||
p={3}
|
p={3}
|
||||||
border={theme.borders.base}
|
border={theme.borders.base}
|
||||||
boxShadow={'sm'}
|
boxShadow={'sm'}
|
||||||
bg={'myBlue.300'}
|
h={'80px'}
|
||||||
|
cursor={'pointer'}
|
||||||
|
_hover={{
|
||||||
|
boxShadow: 'md'
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (item.type === KbTypeEnum.folder) {
|
||||||
|
setParentId(item._id);
|
||||||
|
} else if (item.type === KbTypeEnum.dataset) {
|
||||||
|
const vectorModel = selectedKbList[0]?.vectorModel?.model;
|
||||||
|
|
||||||
|
if (vectorModel && vectorModel !== item.vectorModel.model) {
|
||||||
|
return toast({
|
||||||
|
status: 'warning',
|
||||||
|
title: '仅能选择同一个索引模型的知识库'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setSelectedKbList((state) => [
|
||||||
|
...state,
|
||||||
|
{ kbId: item._id, vectorModel: item.vectorModel }
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Flex alignItems={'center'} h={'38px'}>
|
<Flex alignItems={'center'} h={'38px'}>
|
||||||
<Avatar src={item.avatar} w={['24px', '28px']}></Avatar>
|
<Avatar src={item.avatar} w={['24px', '28px']}></Avatar>
|
||||||
<Box flex={'1 0 0'} mx={3}>
|
<Box
|
||||||
|
className="textEllipsis"
|
||||||
|
ml={3}
|
||||||
|
fontWeight={'bold'}
|
||||||
|
fontSize={['md', 'lg', 'xl']}
|
||||||
|
>
|
||||||
{item.name}
|
{item.name}
|
||||||
</Box>
|
</Box>
|
||||||
<MyIcon
|
</Flex>
|
||||||
name={'delete'}
|
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
|
||||||
w={'14px'}
|
{item.type === KbTypeEnum.folder ? (
|
||||||
cursor={'pointer'}
|
<Box color={'myGray.500'}>{t('Folder')}</Box>
|
||||||
_hover={{ color: 'red.500' }}
|
) : (
|
||||||
onClick={() => {
|
<>
|
||||||
setSelectedKbList((state) => state.filter((kb) => kb.kbId !== item._id));
|
<MyIcon mr={1} name="kbTest" w={'12px'} />
|
||||||
}}
|
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
|
||||||
/>
|
</>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
</Card>
|
</Card>
|
||||||
);
|
</MyTooltip>
|
||||||
})()
|
);
|
||||||
)}
|
})()
|
||||||
</Grid>
|
|
||||||
|
|
||||||
{filterKbList.selected.length > 0 && <Divider my={3} />}
|
|
||||||
|
|
||||||
<Grid
|
|
||||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
|
|
||||||
gridGap={3}
|
|
||||||
>
|
|
||||||
{filterKbList.unSelected.map((item) =>
|
|
||||||
(() => {
|
|
||||||
return (
|
|
||||||
<MyTooltip
|
|
||||||
key={item._id}
|
|
||||||
label={
|
|
||||||
item.type === KbTypeEnum.dataset
|
|
||||||
? t('kb.Select Dataset')
|
|
||||||
: t('kb.Select Folder')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<Card
|
|
||||||
p={3}
|
|
||||||
border={theme.borders.base}
|
|
||||||
boxShadow={'sm'}
|
|
||||||
h={'80px'}
|
|
||||||
cursor={'pointer'}
|
|
||||||
_hover={{
|
|
||||||
boxShadow: 'md'
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
if (item.type === KbTypeEnum.folder) {
|
|
||||||
setParentId(item._id);
|
|
||||||
} else if (item.type === KbTypeEnum.dataset) {
|
|
||||||
const vectorModel = selectedKbList[0]?.vectorModel?.model;
|
|
||||||
|
|
||||||
if (vectorModel && vectorModel !== item.vectorModel.model) {
|
|
||||||
return toast({
|
|
||||||
status: 'warning',
|
|
||||||
title: '仅能选择同一个索引模型的知识库'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setSelectedKbList((state) => [
|
|
||||||
...state,
|
|
||||||
{ kbId: item._id, vectorModel: item.vectorModel }
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<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 === KbTypeEnum.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>
|
|
||||||
{filterKbList.unSelected.length === 0 && (
|
|
||||||
<Flex mt={5} flexDirection={'column'} alignItems={'center'}>
|
|
||||||
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
|
||||||
<Box mt={2} color={'myGray.500'}>
|
|
||||||
这个目录已经没东西可选了~
|
|
||||||
</Box>
|
|
||||||
</Flex>
|
|
||||||
)}
|
)}
|
||||||
</ModalBody>
|
</Grid>
|
||||||
|
{filterKbList.unSelected.length === 0 && (
|
||||||
|
<Flex mt={5} flexDirection={'column'} alignItems={'center'}>
|
||||||
|
<MyIcon name="empty" w={'48px'} h={'48px'} color={'transparent'} />
|
||||||
|
<Box mt={2} color={'myGray.500'}>
|
||||||
|
这个目录已经没东西可选了~
|
||||||
|
</Box>
|
||||||
|
</Flex>
|
||||||
|
)}
|
||||||
|
</ModalBody>
|
||||||
|
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// filter out the kb that is not in the kList
|
// filter out the kb that is not in the kList
|
||||||
const filterKbList = selectedKbList.filter((kb) => {
|
const filterKbList = selectedKbList.filter((kb) => {
|
||||||
return datasets.find((item) => item._id === kb.kbId);
|
return datasets.find((item) => item._id === kb.kbId);
|
||||||
});
|
});
|
||||||
|
|
||||||
onClose();
|
onClose();
|
||||||
onChange(filterKbList);
|
onChange(filterKbList);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
完成
|
完成
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</Flex>
|
</DatasetSelectModal>
|
||||||
</MyModal>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -8,7 +8,7 @@ import { defaultKbDetail } from '@/constants/kb';
|
|||||||
import { KbUpdateParams } from '@/api/request/kb';
|
import { KbUpdateParams } from '@/api/request/kb';
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
datasets: KbListItemType[];
|
allDatasets: KbListItemType[];
|
||||||
loadAllDatasets: () => Promise<KbListItemType[]>;
|
loadAllDatasets: () => Promise<KbListItemType[]>;
|
||||||
myKbList: KbListItemType[];
|
myKbList: KbListItemType[];
|
||||||
loadKbList: (parentId?: string) => Promise<any>;
|
loadKbList: (parentId?: string) => Promise<any>;
|
||||||
@@ -27,11 +27,11 @@ export const useDatasetStore = create<State>()(
|
|||||||
devtools(
|
devtools(
|
||||||
persist(
|
persist(
|
||||||
immer((set, get) => ({
|
immer((set, get) => ({
|
||||||
datasets: [],
|
allDatasets: [],
|
||||||
async loadAllDatasets() {
|
async loadAllDatasets() {
|
||||||
const res = await getAllDataset();
|
const res = await getAllDataset();
|
||||||
set((state) => {
|
set((state) => {
|
||||||
state.datasets = res;
|
state.allDatasets = res;
|
||||||
});
|
});
|
||||||
return res;
|
return res;
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user