mirror of
https://github.com/infiniflow/ragflow.git
synced 2025-10-12 08:28:50 +00:00
### What problem does this PR solve? Feat: Import & export the agents. #3851 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
c76e7b1e28
commit
e64c7dfdf6
34
web/package-lock.json
generated
34
web/package-lock.json
generated
@ -28,6 +28,7 @@
|
|||||||
"@radix-ui/react-switch": "^1.1.1",
|
"@radix-ui/react-switch": "^1.1.1",
|
||||||
"@radix-ui/react-tabs": "^1.1.1",
|
"@radix-ui/react-tabs": "^1.1.1",
|
||||||
"@radix-ui/react-toast": "^1.2.2",
|
"@radix-ui/react-toast": "^1.2.2",
|
||||||
|
"@radix-ui/react-tooltip": "^1.1.4",
|
||||||
"@tailwindcss/line-clamp": "^0.4.4",
|
"@tailwindcss/line-clamp": "^0.4.4",
|
||||||
"@tanstack/react-query": "^5.40.0",
|
"@tanstack/react-query": "^5.40.0",
|
||||||
"@tanstack/react-query-devtools": "^5.51.5",
|
"@tanstack/react-query-devtools": "^5.51.5",
|
||||||
@ -4891,6 +4892,39 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@radix-ui/react-tooltip": {
|
||||||
|
"version": "1.1.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@radix-ui/react-tooltip/-/react-tooltip-1.1.4.tgz",
|
||||||
|
"integrity": "sha512-QpObUH/ZlpaO4YgHSaYzrLO2VuO+ZBFFgGzjMUPwtiYnAzzNNDPJeEGRrT7qNOrWm/Jr08M1vlp+vTHtnSQ0Uw==",
|
||||||
|
"dependencies": {
|
||||||
|
"@radix-ui/primitive": "1.1.0",
|
||||||
|
"@radix-ui/react-compose-refs": "1.1.0",
|
||||||
|
"@radix-ui/react-context": "1.1.1",
|
||||||
|
"@radix-ui/react-dismissable-layer": "1.1.1",
|
||||||
|
"@radix-ui/react-id": "1.1.0",
|
||||||
|
"@radix-ui/react-popper": "1.2.0",
|
||||||
|
"@radix-ui/react-portal": "1.1.2",
|
||||||
|
"@radix-ui/react-presence": "1.1.1",
|
||||||
|
"@radix-ui/react-primitive": "2.0.0",
|
||||||
|
"@radix-ui/react-slot": "1.1.0",
|
||||||
|
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||||
|
"@radix-ui/react-visually-hidden": "1.1.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "*",
|
||||||
|
"@types/react-dom": "*",
|
||||||
|
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||||
|
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@types/react-dom": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@radix-ui/react-use-callback-ref": {
|
"node_modules/@radix-ui/react-use-callback-ref": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
"resolved": "https://registry.npmmirror.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
"@radix-ui/react-switch": "^1.1.1",
|
"@radix-ui/react-switch": "^1.1.1",
|
||||||
"@radix-ui/react-tabs": "^1.1.1",
|
"@radix-ui/react-tabs": "^1.1.1",
|
||||||
"@radix-ui/react-toast": "^1.2.2",
|
"@radix-ui/react-toast": "^1.2.2",
|
||||||
|
"@radix-ui/react-tooltip": "^1.1.4",
|
||||||
"@tailwindcss/line-clamp": "^0.4.4",
|
"@tailwindcss/line-clamp": "^0.4.4",
|
||||||
"@tanstack/react-query": "^5.40.0",
|
"@tanstack/react-query": "^5.40.0",
|
||||||
"@tanstack/react-query-devtools": "^5.51.5",
|
"@tanstack/react-query-devtools": "^5.51.5",
|
||||||
|
30
web/src/components/ui/tooltip.tsx
Normal file
30
web/src/components/ui/tooltip.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import * as TooltipPrimitive from '@radix-ui/react-tooltip';
|
||||||
|
import * as React from 'react';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
const TooltipProvider = TooltipPrimitive.Provider;
|
||||||
|
|
||||||
|
const Tooltip = TooltipPrimitive.Root;
|
||||||
|
|
||||||
|
const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||||
|
|
||||||
|
const TooltipContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
||||||
|
>(({ className, sideOffset = 4, ...props }, ref) => (
|
||||||
|
<TooltipPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
sideOffset={sideOffset}
|
||||||
|
className={cn(
|
||||||
|
'z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger };
|
@ -66,27 +66,28 @@ export const LanguageTranslationMap = {
|
|||||||
Vietnamese: 'vi',
|
Vietnamese: 'vi',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const FileMimeTypeMap = {
|
export enum FileMimeType {
|
||||||
bmp: 'image/bmp',
|
Bmp = 'image/bmp',
|
||||||
csv: 'text/csv',
|
Csv = 'text/csv',
|
||||||
odt: 'application/vnd.oasis.opendocument.text',
|
Odt = 'application/vnd.oasis.opendocument.text',
|
||||||
doc: 'application/msword',
|
Doc = 'application/msword',
|
||||||
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
Docx = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||||
gif: 'image/gif',
|
Gif = 'image/gif',
|
||||||
htm: 'text/htm',
|
Htm = 'text/htm',
|
||||||
html: 'text/html',
|
Html = 'text/html',
|
||||||
jpg: 'image/jpg',
|
Jpg = 'image/jpg',
|
||||||
jpeg: 'image/jpeg',
|
Jpeg = 'image/jpeg',
|
||||||
pdf: 'application/pdf',
|
Pdf = 'application/pdf',
|
||||||
png: 'image/png',
|
Png = 'image/png',
|
||||||
ppt: 'application/vnd.ms-powerpoint',
|
Ppt = 'application/vnd.ms-powerpoint',
|
||||||
pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
Pptx = 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||||
tiff: 'image/tiff',
|
Tiff = 'image/tiff',
|
||||||
txt: 'text/plain',
|
Txt = 'text/plain',
|
||||||
xls: 'application/vnd.ms-excel',
|
Xls = 'application/vnd.ms-excel',
|
||||||
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
Xlsx = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
mp4: 'video/mp4',
|
Mp4 = 'video/mp4',
|
||||||
};
|
Json = 'application/json',
|
||||||
|
}
|
||||||
|
|
||||||
export const Domain = 'demo.ragflow.io';
|
export const Domain = 'demo.ragflow.io';
|
||||||
|
|
||||||
|
@ -1077,6 +1077,8 @@ When you want to search the given knowledge base at first place, set a higher pa
|
|||||||
ccEmailTip: 'cc_email: CC email (Optional)',
|
ccEmailTip: 'cc_email: CC email (Optional)',
|
||||||
subjectTip: 'subject: Email subject (Optional)',
|
subjectTip: 'subject: Email subject (Optional)',
|
||||||
contentTip: 'content: Email content (Optional)',
|
contentTip: 'content: Email content (Optional)',
|
||||||
|
jsonUploadTypeErrorMessage: 'Please upload json file',
|
||||||
|
jsonUploadContentErrorMessage: 'json file error',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: 'All rights reserved @ React',
|
profile: 'All rights reserved @ React',
|
||||||
|
@ -1010,6 +1010,8 @@ export default {
|
|||||||
testRun: '試運行',
|
testRun: '試運行',
|
||||||
template: '模板轉換',
|
template: '模板轉換',
|
||||||
templateDescription: '此元件用於排版各種元件的輸出。 ',
|
templateDescription: '此元件用於排版各種元件的輸出。 ',
|
||||||
|
jsonUploadTypeErrorMessage: '請上傳json檔',
|
||||||
|
jsonUploadContentErrorMessage: 'json 檔案錯誤',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: '“保留所有權利 @ react”',
|
profile: '“保留所有權利 @ react”',
|
||||||
|
@ -1055,6 +1055,8 @@ export default {
|
|||||||
ccEmailTip: 'cc_email: 抄送邮箱(可选)',
|
ccEmailTip: 'cc_email: 抄送邮箱(可选)',
|
||||||
subjectTip: 'subject: 邮件主题(可选)',
|
subjectTip: 'subject: 邮件主题(可选)',
|
||||||
contentTip: 'content: 邮件内容(可选)',
|
contentTip: 'content: 邮件内容(可选)',
|
||||||
|
jsonUploadTypeErrorMessage: '请上传json文件',
|
||||||
|
jsonUploadContentErrorMessage: 'json 文件错误',
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
profile: 'All rights reserved @ React',
|
profile: 'All rights reserved @ React',
|
||||||
|
@ -1,8 +1,16 @@
|
|||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipProvider,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from '@/components/ui/tooltip';
|
||||||
import { useSetModalState } from '@/hooks/common-hooks';
|
import { useSetModalState } from '@/hooks/common-hooks';
|
||||||
|
import { FolderInput, FolderOutput } from 'lucide-react';
|
||||||
import { useCallback, useEffect } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import ReactFlow, {
|
import ReactFlow, {
|
||||||
Background,
|
Background,
|
||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
|
ControlButton,
|
||||||
Controls,
|
Controls,
|
||||||
NodeMouseHandler,
|
NodeMouseHandler,
|
||||||
} from 'reactflow';
|
} from 'reactflow';
|
||||||
@ -13,12 +21,14 @@ import FormDrawer from '../flow-drawer';
|
|||||||
import {
|
import {
|
||||||
useGetBeginNodeDataQuery,
|
useGetBeginNodeDataQuery,
|
||||||
useHandleDrop,
|
useHandleDrop,
|
||||||
|
useHandleExportOrImportJsonFile,
|
||||||
useSelectCanvasData,
|
useSelectCanvasData,
|
||||||
useShowFormDrawer,
|
useShowFormDrawer,
|
||||||
useValidateConnection,
|
useValidateConnection,
|
||||||
useWatchNodeFormDataChange,
|
useWatchNodeFormDataChange,
|
||||||
} from '../hooks';
|
} from '../hooks';
|
||||||
import { BeginQuery } from '../interface';
|
import { BeginQuery } from '../interface';
|
||||||
|
import JsonUploadModal from '../json-upload-modal';
|
||||||
import RunDrawer from '../run-drawer';
|
import RunDrawer from '../run-drawer';
|
||||||
import { ButtonEdge } from './edge';
|
import { ButtonEdge } from './edge';
|
||||||
import styles from './index.less';
|
import styles from './index.less';
|
||||||
@ -115,6 +125,14 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
|
|
||||||
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
const getBeginNodeDataQuery = useGetBeginNodeDataQuery();
|
||||||
|
|
||||||
|
const {
|
||||||
|
handleExportJson,
|
||||||
|
handleImportJson,
|
||||||
|
fileUploadVisible,
|
||||||
|
onFileUploadOk,
|
||||||
|
hideFileUploadModal,
|
||||||
|
} = useHandleExportOrImportJsonFile();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (drawerVisible) {
|
if (drawerVisible) {
|
||||||
const query: BeginQuery[] = getBeginNodeDataQuery();
|
const query: BeginQuery[] = getBeginNodeDataQuery();
|
||||||
@ -192,7 +210,28 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
deleteKeyCode={['Delete', 'Backspace']}
|
deleteKeyCode={['Delete', 'Backspace']}
|
||||||
>
|
>
|
||||||
<Background />
|
<Background />
|
||||||
<Controls />
|
<Controls>
|
||||||
|
<ControlButton onClick={handleImportJson}>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<FolderInput />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>Import</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</ControlButton>
|
||||||
|
<ControlButton onClick={handleExportJson}>
|
||||||
|
<TooltipProvider>
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<FolderOutput />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>Export</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</TooltipProvider>
|
||||||
|
</ControlButton>
|
||||||
|
</Controls>
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
{formDrawerVisible && (
|
{formDrawerVisible && (
|
||||||
<FormDrawer
|
<FormDrawer
|
||||||
@ -214,6 +253,13 @@ function FlowCanvas({ drawerVisible, hideDrawer }: IProps) {
|
|||||||
showModal={showChatModal}
|
showModal={showChatModal}
|
||||||
></RunDrawer>
|
></RunDrawer>
|
||||||
)}
|
)}
|
||||||
|
{fileUploadVisible && (
|
||||||
|
<JsonUploadModal
|
||||||
|
onOk={onFileUploadOk}
|
||||||
|
visible={fileUploadVisible}
|
||||||
|
hideModal={hideFileUploadModal}
|
||||||
|
></JsonUploadModal>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -12,14 +12,16 @@ import React, {
|
|||||||
import { Connection, Edge, Node, Position, ReactFlowInstance } from 'reactflow';
|
import { Connection, Edge, Node, Position, ReactFlowInstance } from 'reactflow';
|
||||||
// import { shallow } from 'zustand/shallow';
|
// import { shallow } from 'zustand/shallow';
|
||||||
import { variableEnabledFieldMap } from '@/constants/chat';
|
import { variableEnabledFieldMap } from '@/constants/chat';
|
||||||
|
import { FileMimeType } from '@/constants/common';
|
||||||
import {
|
import {
|
||||||
ModelVariableType,
|
ModelVariableType,
|
||||||
settledModelVariableMap,
|
settledModelVariableMap,
|
||||||
} from '@/constants/knowledge';
|
} from '@/constants/knowledge';
|
||||||
import { useFetchModelId } from '@/hooks/logic-hooks';
|
import { useFetchModelId } from '@/hooks/logic-hooks';
|
||||||
import { Variable } from '@/interfaces/database/chat';
|
import { Variable } from '@/interfaces/database/chat';
|
||||||
|
import { downloadJsonFile } from '@/utils/file-util';
|
||||||
import { useDebounceEffect } from 'ahooks';
|
import { useDebounceEffect } from 'ahooks';
|
||||||
import { FormInstance, message } from 'antd';
|
import { FormInstance, UploadFile, message } from 'antd';
|
||||||
import { DefaultOptionType } from 'antd/es/select';
|
import { DefaultOptionType } from 'antd/es/select';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { humanId } from 'human-id';
|
import { humanId } from 'human-id';
|
||||||
@ -261,30 +263,45 @@ export const useShowFormDrawer = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useSaveGraph = () => {
|
export const useBuildDslData = () => {
|
||||||
const { data } = useFetchFlow();
|
const { data } = useFetchFlow();
|
||||||
const { setFlow, loading } = useSetFlow();
|
|
||||||
const { id } = useParams();
|
|
||||||
const { nodes, edges } = useGraphStore((state) => state);
|
const { nodes, edges } = useGraphStore((state) => state);
|
||||||
useEffect(() => {}, [nodes]);
|
|
||||||
const saveGraph = useCallback(
|
const buildDslData = useCallback(
|
||||||
async (currentNodes?: Node[]) => {
|
(currentNodes?: Node[]) => {
|
||||||
const dslComponents = buildDslComponentsByGraph(
|
const dslComponents = buildDslComponentsByGraph(
|
||||||
currentNodes ?? nodes,
|
currentNodes ?? nodes,
|
||||||
edges,
|
edges,
|
||||||
data.dsl.components,
|
data.dsl.components,
|
||||||
);
|
);
|
||||||
return setFlow({
|
|
||||||
id,
|
return {
|
||||||
title: data.title,
|
|
||||||
dsl: {
|
|
||||||
...data.dsl,
|
...data.dsl,
|
||||||
graph: { nodes: currentNodes ?? nodes, edges },
|
graph: { nodes: currentNodes ?? nodes, edges },
|
||||||
components: dslComponents,
|
components: dslComponents,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
|
[data.dsl, edges, nodes],
|
||||||
|
);
|
||||||
|
|
||||||
|
return { buildDslData };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useSaveGraph = () => {
|
||||||
|
const { data } = useFetchFlow();
|
||||||
|
const { setFlow, loading } = useSetFlow();
|
||||||
|
const { id } = useParams();
|
||||||
|
const { buildDslData } = useBuildDslData();
|
||||||
|
|
||||||
|
const saveGraph = useCallback(
|
||||||
|
async (currentNodes?: Node[]) => {
|
||||||
|
return setFlow({
|
||||||
|
id,
|
||||||
|
title: data.title,
|
||||||
|
dsl: buildDslData(currentNodes),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[nodes, edges, setFlow, id, data],
|
[setFlow, id, data.title, buildDslData],
|
||||||
);
|
);
|
||||||
|
|
||||||
return { saveGraph, loading };
|
return { saveGraph, loading };
|
||||||
@ -774,3 +791,54 @@ export const useWatchAgentChange = (chatDrawerVisible: boolean) => {
|
|||||||
|
|
||||||
return time;
|
return time;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const useHandleExportOrImportJsonFile = () => {
|
||||||
|
const { buildDslData } = useBuildDslData();
|
||||||
|
const {
|
||||||
|
visible: fileUploadVisible,
|
||||||
|
hideModal: hideFileUploadModal,
|
||||||
|
showModal: showFileUploadModal,
|
||||||
|
} = useSetModalState();
|
||||||
|
const setGraphInfo = useSetGraphInfo();
|
||||||
|
const { data } = useFetchFlow();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const onFileUploadOk = useCallback(
|
||||||
|
async (fileList: UploadFile[]) => {
|
||||||
|
if (fileList.length > 0) {
|
||||||
|
const file: File = fileList[0] as unknown as File;
|
||||||
|
if (file.type !== FileMimeType.Json) {
|
||||||
|
message.error(t('flow.jsonUploadTypeErrorMessage'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const graphStr = await file.text();
|
||||||
|
const errorMessage = t('flow.jsonUploadContentErrorMessage');
|
||||||
|
try {
|
||||||
|
const graph = JSON.parse(graphStr);
|
||||||
|
if (graphStr && !isEmpty(graph) && Array.isArray(graph?.nodes)) {
|
||||||
|
setGraphInfo(graph ?? ({} as IGraph));
|
||||||
|
hideFileUploadModal();
|
||||||
|
} else {
|
||||||
|
message.error(errorMessage);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
message.error(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[hideFileUploadModal, setGraphInfo, t],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleExportJson = useCallback(() => {
|
||||||
|
downloadJsonFile(buildDslData().graph, `${data.title}.json`);
|
||||||
|
}, [buildDslData, data.title]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
fileUploadVisible,
|
||||||
|
handleExportJson,
|
||||||
|
handleImportJson: showFileUploadModal,
|
||||||
|
hideFileUploadModal,
|
||||||
|
onFileUploadOk,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
13
web/src/pages/flow/json-upload-modal/index.less
Normal file
13
web/src/pages/flow/json-upload-modal/index.less
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
.uploader {
|
||||||
|
:global {
|
||||||
|
.ant-upload-list {
|
||||||
|
max-height: 40vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.uploadLimit {
|
||||||
|
color: red;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
97
web/src/pages/flow/json-upload-modal/index.tsx
Normal file
97
web/src/pages/flow/json-upload-modal/index.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { useTranslate } from '@/hooks/common-hooks';
|
||||||
|
import { IModalProps } from '@/interfaces/common';
|
||||||
|
import { InboxOutlined } from '@ant-design/icons';
|
||||||
|
import { Modal, Upload, UploadFile, UploadProps } from 'antd';
|
||||||
|
import { Dispatch, SetStateAction, useState } from 'react';
|
||||||
|
|
||||||
|
import { FileMimeType } from '@/constants/common';
|
||||||
|
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
|
const { Dragger } = Upload;
|
||||||
|
|
||||||
|
const FileUpload = ({
|
||||||
|
directory,
|
||||||
|
fileList,
|
||||||
|
setFileList,
|
||||||
|
}: {
|
||||||
|
directory: boolean;
|
||||||
|
fileList: UploadFile[];
|
||||||
|
setFileList: Dispatch<SetStateAction<UploadFile[]>>;
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslate('fileManager');
|
||||||
|
const props: UploadProps = {
|
||||||
|
multiple: false,
|
||||||
|
accept: FileMimeType.Json,
|
||||||
|
onRemove: (file) => {
|
||||||
|
const index = fileList.indexOf(file);
|
||||||
|
const newFileList = fileList.slice();
|
||||||
|
newFileList.splice(index, 1);
|
||||||
|
setFileList(newFileList);
|
||||||
|
},
|
||||||
|
beforeUpload: (file) => {
|
||||||
|
setFileList(() => {
|
||||||
|
return [file];
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
directory,
|
||||||
|
fileList,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dragger {...props} className={styles.uploader}>
|
||||||
|
<p className="ant-upload-drag-icon">
|
||||||
|
<InboxOutlined />
|
||||||
|
</p>
|
||||||
|
<p className="ant-upload-text">{t('uploadTitle')}</p>
|
||||||
|
<p className="ant-upload-hint">{t('uploadDescription')}</p>
|
||||||
|
{false && <p className={styles.uploadLimit}>{t('uploadLimit')}</p>}
|
||||||
|
</Dragger>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const JsonUploadModal = ({
|
||||||
|
visible,
|
||||||
|
hideModal,
|
||||||
|
loading,
|
||||||
|
onOk: onFileUploadOk,
|
||||||
|
}: IModalProps<UploadFile[]>) => {
|
||||||
|
const { t } = useTranslate('fileManager');
|
||||||
|
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||||
|
const [directoryFileList, setDirectoryFileList] = useState<UploadFile[]>([]);
|
||||||
|
|
||||||
|
const clearFileList = () => {
|
||||||
|
setFileList([]);
|
||||||
|
setDirectoryFileList([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOk = async () => {
|
||||||
|
const ret = await onFileUploadOk?.([...fileList, ...directoryFileList]);
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
|
||||||
|
const afterClose = () => {
|
||||||
|
clearFileList();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={t('uploadFile')}
|
||||||
|
open={visible}
|
||||||
|
onOk={onOk}
|
||||||
|
onCancel={hideModal}
|
||||||
|
confirmLoading={loading}
|
||||||
|
afterClose={afterClose}
|
||||||
|
>
|
||||||
|
<FileUpload
|
||||||
|
directory={false}
|
||||||
|
fileList={fileList}
|
||||||
|
setFileList={setFileList}
|
||||||
|
></FileUpload>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default JsonUploadModal;
|
@ -1,3 +1,4 @@
|
|||||||
|
import { FileMimeType } from '@/constants/common';
|
||||||
import fileManagerService from '@/services/file-manager-service';
|
import fileManagerService from '@/services/file-manager-service';
|
||||||
import { UploadFile } from 'antd';
|
import { UploadFile } from 'antd';
|
||||||
|
|
||||||
@ -137,3 +138,11 @@ export const formatBytes = (x: string | number) => {
|
|||||||
|
|
||||||
return n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + Units[l];
|
return n.toFixed(n < 10 && l > 0 ? 1 : 0) + ' ' + Units[l];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const downloadJsonFile = async (
|
||||||
|
data: Record<string, any>,
|
||||||
|
fileName: string,
|
||||||
|
) => {
|
||||||
|
const blob = new Blob([JSON.stringify(data)], { type: FileMimeType.Json });
|
||||||
|
downloadFileFromBlob(blob, fileName);
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user