Skip to content

Commit 00b5f2e

Browse files
newfish-cmykc121914yu
authored andcommitted
fix dataset quick create modal (#5949)
* fix dataset quick create modal * fix ui
1 parent 8d40b8b commit 00b5f2e

File tree

11 files changed

+368
-138
lines changed

11 files changed

+368
-138
lines changed

packages/web/i18n/en/app.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@
129129
"dataset": "dataset",
130130
"dataset.Select_dataset_model_tip": "Only knowledge bases with the same index model can be selected",
131131
"dataset.create_dataset_tips": "For more advanced operations, please go to",
132+
"dataset_create_failed": "Knowledge base creation failed",
132133
"dataset_create_success": "The knowledge base was created successfully and files are being indexed in the background.",
133134
"dataset_empty_tips": "You don’t have a knowledge base yet, create one first.",
134135
"dataset_search_tool_description": "Call the \"Semantic Search\" and \"Full-text Search\" capabilities to find reference content that may be related to the problem from the \"Knowledge Base\". \nPrioritize calling this tool to assist in answering user questions.",

packages/web/i18n/zh-CN/app.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@
132132
"dataset": "知识库",
133133
"dataset.Select_dataset_model_tip": "仅能选择同一个索引模型的知识库",
134134
"dataset.create_dataset_tips": "更多高级操作请前往",
135+
"dataset_create_failed": "知识库创建失败",
135136
"dataset_create_success": "知识库创建成功,正在后台索引文件",
136137
"dataset_empty_tips": "你还没有知识库,先创建一个吧",
137138
"dataset_search_tool_description": "调用“语义检索”和“全文检索”能力,从“知识库”中查找可能与问题相关的参考内容。优先调用该工具来辅助回答用户的问题。",

packages/web/i18n/zh-Hant/app.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@
128128
"dataset": "知識庫",
129129
"dataset.Select_dataset_model_tip": "僅能選擇同一個索引模型的知識庫",
130130
"dataset.create_dataset_tips": "更多高級操作請前往",
131+
"dataset_create_failed": "知識庫創建失敗",
131132
"dataset_create_success": "知識庫創建成功,正在後台索引文件",
132133
"dataset_empty_tips": "你還沒有知識庫,先創建一個吧",
133134
"dataset_search_tool_description": "呼叫「語意搜尋」和「全文搜尋」功能,從「知識庫」中尋找可能與問題相關的參考內容。優先呼叫這個工具來協助回答使用者的問題。",

projects/app/src/components/Layout/navbar.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ const Navbar = ({ unread }: { unread: number }) => {
110110
[lastChatAppId, lastPane, t, userInfo?.username]
111111
);
112112

113-
const isSecondNavbarPage = useMemo(() => {
114-
return ['/plugin'].includes(router.pathname);
113+
const isDashboardPage = useMemo(() => {
114+
return router.pathname.startsWith('/dashboard');
115115
}, [router.pathname]);
116116

117117
return (
@@ -123,7 +123,7 @@ const Navbar = ({ unread }: { unread: number }) => {
123123
w={'100%'}
124124
userSelect={'none'}
125125
pb={2}
126-
bg={isSecondNavbarPage ? 'white' : 'transparent'}
126+
bg={isDashboardPage ? 'white' : 'transparent'}
127127
>
128128
{/* logo */}
129129
<Box flex={'0 0 auto'} mb={3}>
@@ -147,7 +147,7 @@ const Navbar = ({ unread }: { unread: number }) => {
147147
: {
148148
bg: 'transparent',
149149
_hover: {
150-
bg: isSecondNavbarPage ? 'white' : 'rgba(255,255,255,0.9)'
150+
bg: isDashboardPage ? 'white' : 'rgba(255,255,255,0.9)'
151151
}
152152
})}
153153
{...(item.link !== router.asPath

projects/app/src/components/core/app/DatasetSelectModal.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import SearchInput from '@fastgpt/web/components/common/Input/SearchInput';
2727
import { useDatasetSelect } from '@/components/core/dataset/SelectModal';
2828
import FolderPath from '@/components/common/folder/Path';
2929
import EmptyTip from '@fastgpt/web/components/common/EmptyTip';
30-
import QuickCreateDatasetModal from '@/pageComponents/app/detail/components/QuickCreateModal';
30+
import QuickCreateDatasetModal from '@/pageComponents/app/detail/components/QuickCreateDatasetModal';
31+
import { useUserStore } from '@/web/support/user/useUserStore';
3132

3233
// Dataset selection modal component
3334
export const DatasetSelectModal = ({
@@ -47,6 +48,7 @@ export const DatasetSelectModal = ({
4748
const [selectedDatasets, setSelectedDatasets] =
4849
useState<SelectedDatasetType[]>(defaultSelectedDatasets);
4950
const { toast } = useToast();
51+
const { userInfo } = useUserStore();
5052

5153
// Use server-side search, following the logic of the dataset list page
5254
const {
@@ -154,9 +156,11 @@ export const DatasetSelectModal = ({
154156
<Flex h="100%" direction="column" flex={1} overflow="hidden" minH={0}>
155157
<ModalBody flex={1} h={0} overflow="hidden">
156158
{isRootEmpty ? (
157-
<VStack mt={8}>
159+
<VStack h={'full'} justifyContent={'center'}>
158160
<EmptyTip text={t('app:dataset_empty_tips')} py={4} />
159-
<Button onClick={onOpenQuickCreate}>{t('common:Create')}</Button>
161+
{userInfo?.team?.permission.hasDatasetCreatePer && (
162+
<Button onClick={onOpenQuickCreate}>{t('common:Create')}</Button>
163+
)}
160164
</VStack>
161165
) : (
162166
<>
@@ -427,7 +431,7 @@ export const DatasetSelectModal = ({
427431
{/* Modal footer button area */}
428432
<ModalFooter>
429433
<HStack spacing={4} w="full" align="center">
430-
{!isRootEmpty && (
434+
{!isRootEmpty && userInfo?.team?.permission.hasDatasetCreatePer && (
431435
<Button
432436
leftIcon={<MyIcon name="common/addLight" w={4} />}
433437
variant={'transparentBase'}

projects/app/src/pageComponents/app/detail/components/QuickCreateModal.tsx renamed to projects/app/src/pageComponents/app/detail/components/QuickCreateDatasetModal.tsx

Lines changed: 108 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useState, useMemo } from 'react';
22
import { useTranslation } from 'next-i18next';
33
import { useForm } from 'react-hook-form';
44
import {
@@ -18,21 +18,10 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
1818
import MyIcon from '@fastgpt/web/components/common/Icon';
1919
import { useUploadAvatar } from '@fastgpt/web/common/file/hooks/useUploadAvatar';
2020
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
21-
import {
22-
postCreateDataset,
23-
getDatasetById,
24-
postCreateDatasetFileCollection
25-
} from '@/web/core/dataset/api';
21+
import { postCreateDatasetWithFiles, getDatasetById } from '@/web/core/dataset/api';
2622
import { getUploadAvatarPresignedUrl } from '@/web/common/file/api';
2723
import { uploadFile2DB } from '@/web/common/file/controller';
2824
import { useSystemStore } from '@/web/common/system/useSystemStore';
29-
import {
30-
ChunkSettingModeEnum,
31-
ChunkTriggerConfigTypeEnum,
32-
DataChunkSplitModeEnum,
33-
DatasetCollectionDataProcessModeEnum,
34-
DatasetTypeEnum
35-
} from '@fastgpt/global/core/dataset/constants';
3625
import { getWebDefaultEmbeddingModel, getWebDefaultLLMModel } from '@/web/common/system/utils';
3726
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
3827
import { getErrText } from '@fastgpt/global/common/error/utils';
@@ -66,6 +55,11 @@ const QuickCreateDatasetModal = ({
6655

6756
const [selectFiles, setSelectFiles] = useState<ImportSourceItemType[]>([]);
6857

58+
const successFiles = useMemo(
59+
() => selectFiles.filter((item) => item.dbFileId && !item.errorMsg),
60+
[selectFiles]
61+
);
62+
6963
const { register, handleSubmit, watch, setValue } = useForm({
7064
defaultValues: {
7165
parentId,
@@ -83,103 +77,106 @@ const QuickCreateDatasetModal = ({
8377
}
8478
});
8579

86-
const handleSelectFiles = (files: SelectFileItemType[]) => {
87-
setSelectFiles((state) => [
88-
...state,
89-
...files.map<ImportSourceItemType>((selectFile) => {
90-
const { fileId, file } = selectFile;
91-
92-
return {
93-
id: fileId,
94-
createStatus: 'waiting',
95-
file,
96-
sourceName: file.name,
97-
sourceSize: formatFileSize(file.size),
98-
icon: getFileIcon(file.name),
99-
uploadedFileRate: 0
100-
};
101-
})
102-
]);
103-
};
80+
const { runAsync: handleSelectFiles, loading: uploading } = useRequest2(
81+
async (files: SelectFileItemType[]) => {
82+
await Promise.all(
83+
files.map(async ({ fileId, file }) => {
84+
try {
85+
const { fileId: uploadFileId } = await uploadFile2DB({
86+
file,
87+
bucketName: BucketNameEnum.dataset,
88+
data: { datasetId: '' },
89+
percentListen: (percent) => {
90+
setSelectFiles((state) =>
91+
state.map((item) =>
92+
item.id === fileId
93+
? {
94+
...item,
95+
uploadedFileRate: item.uploadedFileRate
96+
? Math.max(percent, item.uploadedFileRate)
97+
: percent
98+
}
99+
: item
100+
)
101+
);
102+
}
103+
});
104104

105-
const uploadSingleFile = async (fileItem: ImportSourceItemType, datasetId: string) => {
106-
try {
107-
if (!fileItem.file) return;
108-
setSelectFiles((prev) =>
109-
prev.map((item) => (item.id === fileItem.id ? { ...item, uploadedFileRate: 0 } : item))
105+
setSelectFiles((state) =>
106+
state.map((item) =>
107+
item.id === fileId
108+
? {
109+
...item,
110+
dbFileId: uploadFileId,
111+
isUploading: false,
112+
uploadedFileRate: 100
113+
}
114+
: item
115+
)
116+
);
117+
} catch (error) {
118+
setSelectFiles((state) =>
119+
state.map((item) =>
120+
item.id === fileId
121+
? {
122+
...item,
123+
isUploading: false,
124+
errorMsg: getErrText(error)
125+
}
126+
: item
127+
)
128+
);
129+
}
130+
})
110131
);
132+
},
133+
{
134+
manual: true,
135+
onBefore([files]) {
136+
setSelectFiles((state) => [
137+
...state,
138+
...files.map<ImportSourceItemType>((selectFile) => {
139+
const { fileId, file } = selectFile;
111140

112-
const { fileId } = await uploadFile2DB({
113-
file: fileItem.file,
114-
bucketName: BucketNameEnum.dataset,
115-
data: { datasetId },
116-
percentListen: (percent) => {
117-
setSelectFiles((prev) =>
118-
prev.map((item) =>
119-
item.id === fileItem.id
120-
? { ...item, uploadedFileRate: Math.max(percent, item.uploadedFileRate || 0) }
121-
: item
122-
)
123-
);
124-
}
125-
});
126-
127-
await postCreateDatasetFileCollection({
128-
datasetId,
129-
fileId,
130-
trainingType: DatasetCollectionDataProcessModeEnum.chunk,
131-
chunkTriggerType: ChunkTriggerConfigTypeEnum.minSize,
132-
chunkTriggerMinSize: 1000,
133-
chunkSettingMode: ChunkSettingModeEnum.auto,
134-
chunkSplitMode: DataChunkSplitModeEnum.paragraph,
135-
chunkSize: 1024,
136-
indexSize: 512,
137-
customPdfParse: false
138-
});
139-
140-
setSelectFiles((prev) =>
141-
prev.map((item) =>
142-
item.id === fileItem.id ? { ...item, dbFileId: fileId, uploadedFileRate: 100 } : item
143-
)
144-
);
145-
} catch (error) {
146-
setSelectFiles((prev) =>
147-
prev.map((item) =>
148-
item.id === fileItem.id ? { ...item, errorMsg: getErrText(error) } : item
149-
)
150-
);
141+
return {
142+
id: fileId,
143+
createStatus: 'waiting',
144+
file,
145+
sourceName: file.name,
146+
sourceSize: formatFileSize(file.size),
147+
icon: getFileIcon(file.name),
148+
isUploading: true,
149+
uploadedFileRate: 0
150+
};
151+
})
152+
]);
153+
}
151154
}
152-
};
155+
);
153156

154157
const { runAsync: onCreate, loading: isCreating } = useRequest2(
155158
async (data) => {
156-
const datasetId = await postCreateDataset({
157-
name: data.name.trim(),
158-
avatar: data.avatar,
159-
intro: '',
160-
parentId,
161-
type: DatasetTypeEnum.dataset,
162-
vectorModel: defaultVectorModel,
163-
agentModel: defaultAgentModel,
164-
vlmModel: defaultVLLM
159+
return await postCreateDatasetWithFiles({
160+
datasetParams: {
161+
name: data.name.trim(),
162+
avatar: data.avatar,
163+
parentId,
164+
vectorModel: defaultVectorModel,
165+
agentModel: defaultAgentModel,
166+
vlmModel: defaultVLLM
167+
},
168+
files: selectFiles
169+
.filter((item) => item.dbFileId && !item.errorMsg)
170+
.map((item) => ({
171+
fileId: item.dbFileId!,
172+
name: item.sourceName
173+
}))
165174
});
166-
167-
if (selectFiles.length > 0) {
168-
await Promise.all(selectFiles.map((file) => uploadSingleFile(file, datasetId)));
169-
}
170-
171-
const datasetDetail = await getDatasetById(datasetId);
172-
173-
return {
174-
datasetId,
175-
name: datasetDetail.name,
176-
avatar: datasetDetail.avatar,
177-
vectorModel: datasetDetail.vectorModel
178-
};
179175
},
180176
{
181177
manual: true,
182178
successToast: t('app:dataset_create_success'),
179+
errorToast: t('app:dataset_create_failed'),
183180
onSuccess: (result) => {
184181
onSuccess(result);
185182
onClose();
@@ -198,18 +195,19 @@ const QuickCreateDatasetModal = ({
198195
>
199196
<ModalBody py={6} minH={'500px'}>
200197
<Box mb={6}>
201-
<FormLabel mb={2}>{t('common:app_icon_and_name')}</FormLabel>
198+
<FormLabel mb={2}>{t('common:input_name')}</FormLabel>
202199
<Flex alignItems={'center'}>
203200
<MyTooltip label={t('common:set_avatar')}>
204-
<Avatar
205-
src={avatar}
206-
w={9}
207-
h={9}
208-
mr={4}
209-
borderRadius={'8px'}
210-
cursor={'pointer'}
211-
onClick={handleAvatarSelectorOpen}
212-
/>
201+
<Box w={9} h={9} mr={4}>
202+
<Avatar
203+
src={avatar}
204+
w={'full'}
205+
h={'full'}
206+
borderRadius={'8px'}
207+
cursor={'pointer'}
208+
onClick={handleAvatarSelectorOpen}
209+
/>
210+
</Box>
213211
</MyTooltip>
214212
<FormControl flex={1}>
215213
<Input
@@ -288,7 +286,7 @@ const QuickCreateDatasetModal = ({
288286
) : null}
289287
</Flex>
290288
<Flex w={1 / 5} justifyContent={'end'}>
291-
{!item.uploadedFileRate && (
289+
{!item.isUploading && (
292290
<Flex alignItems={'center'} justifyContent={'center'} w={6} h={6}>
293291
<MyIcon
294292
name={'delete'}
@@ -331,7 +329,7 @@ const QuickCreateDatasetModal = ({
331329
</Button>
332330
<Button
333331
isLoading={isCreating}
334-
isDisabled={selectFiles.length === 0}
332+
isDisabled={successFiles.length === 0 || uploading}
335333
onClick={handleSubmit(onCreate)}
336334
>
337335
{t('common:Create')}

0 commit comments

Comments
 (0)