diff --git a/client/src/components/ChatBox/SelectDataset.tsx b/client/src/components/ChatBox/SelectDataset.tsx index 8d2f59727..c9720e1c4 100644 --- a/client/src/components/ChatBox/SelectDataset.tsx +++ b/client/src/components/ChatBox/SelectDataset.tsx @@ -1,58 +1,43 @@ -import React, { useRef, useState } from 'react'; -import { - ModalBody, - useTheme, - ModalFooter, - Button, - ModalHeader, - Box, - Card, - Flex -} from '@chakra-ui/react'; -import MyModal from '../MyModal'; +import React, { useState } from 'react'; +import { ModalBody, useTheme, ModalFooter, Button, Box, Card, Flex, Grid } from '@chakra-ui/react'; import { useTranslation } from 'next-i18next'; -import { useQuery } from '@tanstack/react-query'; -import { useDatasetStore } from '@/store/dataset'; import { useToast } from '@/hooks/useToast'; import Avatar from '../Avatar'; 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 = ({ + isOpen, onSuccess, onClose }: { + isOpen: boolean; onSuccess: (kbId: string) => void; onClose: () => void; }) => { const { t } = useTranslation(); const theme = useTheme(); - const { isPc } = useGlobalStore(); const { toast } = useToast(); - const { myKbList, loadKbList } = useDatasetStore(); const [selectedId, setSelectedId] = useState(); - - useQuery(['loadKbList'], () => loadKbList()); + const { paths, parentId, setParentId, datasets } = useDatasetSelect(); return ( - - - - {t('chat.Select Mark Kb')} - - {t('chat.Select Mark Kb Desc')} - - - + + - {myKbList.map((item) => + {datasets.map((item) => (() => { const selected = selectedId === item._id; return ( @@ -72,7 +57,11 @@ const SelectDataset = ({ } : {})} onClick={() => { - setSelectedId(item._id); + if (item.type === KbTypeEnum.folder) { + setParentId(item._id); + } else { + setSelectedId(item._id); + } }} > @@ -89,28 +78,36 @@ const SelectDataset = ({ ); })() )} - - - - + - - - + onSuccess(selectedId); + }} + > + {t('Confirm')} + + + ); }; diff --git a/client/src/components/ChatBox/index.tsx b/client/src/components/ChatBox/index.tsx index 417da33e0..e57142213 100644 --- a/client/src/components/ChatBox/index.tsx +++ b/client/src/components/ChatBox/index.tsx @@ -985,13 +985,12 @@ const ChatBox = ( /> )} {/* select one dataset to insert markData */} - {adminMarkData && !adminMarkData.kbId && ( - setAdminMarkData(undefined)} - // @ts-ignore - onSuccess={(kbId) => setAdminMarkData((state) => ({ ...state, kbId }))} - /> - )} + setAdminMarkData(undefined)} + // @ts-ignore + onSuccess={(kbId) => setAdminMarkData((state) => ({ ...state, kbId }))} + /> {/* edit markData modal */} {adminMarkData && adminMarkData.kbId && ( console.log(error)); }, [name]); - return name ? ( + return !!name && !!iconPaths[name] ? ( ; + paths: PathItemType[]; + onClose: () => void; + tips?: string | null; + children: React.ReactNode; +}) => { + const { t } = useTranslation(); + const { isPc } = useGlobalStore(); + + return ( + + + + {!!parentId ? ( + + {paths.map((item, i) => ( + + { + setParentId(item.parentId); + } + })} + > + {item.parentName} + + {i !== paths.length - 1 && ( + + )} + + ))} + + ) : ( + {t('chat.Select Mark Kb')} + )} + {!!tips && ( + + {tips} + + )} + + {children} + + + ); +}; + +export const useDatasetSelect = () => { + const { t } = useTranslation(); + const [parentId, setParentId] = useState(); + + 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; diff --git a/client/src/pages/app/detail/components/BasicEdit/index.tsx b/client/src/pages/app/detail/components/BasicEdit/index.tsx index d4e6c5333..4562729ba 100644 --- a/client/src/pages/app/detail/components/BasicEdit/index.tsx +++ b/client/src/pages/app/detail/components/BasicEdit/index.tsx @@ -71,7 +71,7 @@ const Settings = ({ appId }: { appId: string }) => { const { t } = useTranslation(); const { toast } = useToast(); const { appDetail, updateAppDetail } = useUserStore(); - const { loadAllDatasets, datasets } = useDatasetStore(); + const { loadAllDatasets, allDatasets } = useDatasetStore(); const { isPc } = useGlobalStore(); const [editVariable, setEditVariable] = useState(); @@ -131,8 +131,8 @@ const Settings = ({ appId }: { appId: string }) => { ); }, [getValues, refresh]); const selectedKbList = useMemo( - () => datasets.filter((item) => kbList.find((kb) => kb.kbId === item._id)), - [datasets, kbList] + () => allDatasets.filter((item) => kbList.find((kb) => kb.kbId === item._id)), + [allDatasets, kbList] ); /* 点击删除 */ @@ -564,16 +564,15 @@ const Settings = ({ appId }: { appId: string }) => { defaultData={getValues('chatModel')} /> )} - {isOpenKbSelect && ( - ({ - kbId: item._id, - vectorModel: item.vectorModel - }))} - onClose={onCloseKbSelect} - onChange={replaceKbList} - /> - )} + ({ + kbId: item._id, + vectorModel: item.vectorModel + }))} + onClose={onCloseKbSelect} + onChange={replaceKbList} + /> {isOpenKbParams && ( void; onClose: () => void; @@ -47,229 +47,156 @@ export const KBSelectModal = ({ const { t } = useTranslation(); const theme = useTheme(); const [selectedKbList, setSelectedKbList] = useState(activeKbs); - const { isPc } = useGlobalStore(); const { toast } = useToast(); - const [parentId, setParentId] = useState(); - const { myKbList, loadKbList, datasets, loadAllDatasets } = useDatasetStore(); + const { paths, parentId, setParentId, datasets } = useDatasetSelect(); + const { allDatasets, loadAllDatasets } = useDatasetStore(); - const { data } = useQuery(['loadKbList', parentId], () => { - return Promise.all([loadKbList(parentId), getKbPaths(parentId)]); - }); useQuery(['loadAllDatasets'], loadAllDatasets); - const paths = useMemo( - () => [ - { - parentId: '', - parentName: t('kb.My Dataset') - }, - ...(data?.[1] || []) - ], - [data, t] - ); + const filterKbList = useMemo(() => { return { - selected: datasets.filter((item) => selectedKbList.find((kb) => kb.kbId === item._id)), - unSelected: myKbList.filter((item) => !selectedKbList.find((kb) => kb.kbId === item._id)) + selected: allDatasets.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 ( - - - - {!!parentId ? ( - - {paths.map((item, i) => ( - - { - setParentId(item.parentId); - } - })} - > - {item.parentName} - - {i !== paths.length - 1 && ( - - )} - - ))} - - ) : ( - 关联的知识库({selectedKbList.length}) + + + {filterKbList.selected.map((item) => + (() => { + return ( + + + + + {item.name} + + { + setSelectedKbList((state) => state.filter((kb) => kb.kbId !== item._id)); + }} + /> + + + ); + })() )} - {isPc && ( - - 仅能选择同一个索引模型的知识库 - - )} - + - - - {filterKbList.selected.map((item) => - (() => { - return ( + {filterKbList.selected.length > 0 && } + + + {filterKbList.unSelected.map((item) => + (() => { + return ( + { + 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 } + ]); + } + }} > - + {item.name} - { - setSelectedKbList((state) => state.filter((kb) => kb.kbId !== item._id)); - }} - /> + + + {item.type === KbTypeEnum.folder ? ( + {t('Folder')} + ) : ( + <> + + {item.vectorModel.name} + + )} - ); - })() - )} - - - {filterKbList.selected.length > 0 && } - - - {filterKbList.unSelected.map((item) => - (() => { - return ( - - { - 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 } - ]); - } - }} - > - - - - {item.name} - - - - {item.type === KbTypeEnum.folder ? ( - {t('Folder')} - ) : ( - <> - - {item.vectorModel.name} - - )} - - - - ); - })() - )} - - {filterKbList.unSelected.length === 0 && ( - - - - 这个目录已经没东西可选了~ - - + + ); + })() )} - + + {filterKbList.unSelected.length === 0 && ( + + + + 这个目录已经没东西可选了~ + + + )} + - - - - - + onClose(); + onChange(filterKbList); + }} + > + 完成 + + + ); }; diff --git a/client/src/store/dataset.ts b/client/src/store/dataset.ts index 28c045fbc..a7a3afb13 100644 --- a/client/src/store/dataset.ts +++ b/client/src/store/dataset.ts @@ -8,7 +8,7 @@ import { defaultKbDetail } from '@/constants/kb'; import { KbUpdateParams } from '@/api/request/kb'; type State = { - datasets: KbListItemType[]; + allDatasets: KbListItemType[]; loadAllDatasets: () => Promise; myKbList: KbListItemType[]; loadKbList: (parentId?: string) => Promise; @@ -27,11 +27,11 @@ export const useDatasetStore = create()( devtools( persist( immer((set, get) => ({ - datasets: [], + allDatasets: [], async loadAllDatasets() { const res = await getAllDataset(); set((state) => { - state.datasets = res; + state.allDatasets = res; }); return res; },