feat: Bring In Template (#1975)

# Description

Please include a summary of the change and which issue is fixed. Please
also include relevant motivation and context. List any dependencies that
are required for this change.

# How Has This Been Tested?

Please describe the tests that you ran to verify your changes. Provide
instructions so we can reproduce. Please also list any relevant details
for your test configuration

# Snapshots:

Include snapshots for easier review.

# Checklist:

- [ ] My code follows the style guidelines of this project
- [ ] I have already rebased the commits and make the commit message
conform to the project standard.
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] Any dependent changes have been merged and published in downstream
modules
This commit is contained in:
Dreammy23 2024-09-05 02:13:52 +08:00 committed by GitHub
commit 59bbe9ba7a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 145 additions and 43 deletions

View File

@ -67,14 +67,14 @@ export const downloadFile = (fileId: string) => {
return GET<null, any>(`/api/v2/serve/file/files/dbgpt/${fileId}`);
};
export const getFlowTemplateList = () => {
return GET<null, Array<any>>('/api/v2/serve/awel/flow/templates');
};
export const getFlowTemplateById = (id: string) => {
return GET<null, any>(`/api/v2/serve/awel/flow/templates/${id}`);
};
export const getFlowTemplates = () => {
return GET<null, any>(`/api/v2/serve/awel/flow/templates`);
};
export const getKeys = (data?: IGetKeysRequestParams) => {
return GET<IGetKeysRequestParams, Array<IGetKeysResponseData>>('/api/v2/serve/awel/variables/keys', data);
};

View File

@ -5,6 +5,7 @@ import { Button, Form, GetProp, Modal, Radio, Upload, UploadFile, UploadProps, m
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { Edge, Node } from 'reactflow';
type Props = {
isImportModalOpen: boolean;
setNodes: React.Dispatch<React.SetStateAction<Node<any, string | undefined>[]>>;
@ -39,7 +40,7 @@ export const ImportFlowModal: React.FC<Props> = ({ isImportModalOpen, setIsImpor
if (res?.success) {
messageApi.success(t('Import_Flow_Success'));
localStorage.setItem('importFlowData', JSON.stringify(res?.data));
CanvasWrapper(res?.data);
CanvasWrapper();
} else if (res?.err_msg) {
messageApi.error(res?.err_msg);
}

View File

@ -2,3 +2,4 @@ export * from './add-flow-variable-modal';
export * from './export-flow-modal';
export * from './import-flow-modal';
export * from './save-flow-modal';
export * from './template-flow-modal';

View File

@ -0,0 +1,78 @@
import { getFlowTemplates } from '@/client/api';
import CanvasWrapper from '@/pages/construct/flow/canvas/index';
import type { TableProps } from 'antd';
import { Button, Modal, Space, Table } from 'antd';
import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
type Props = {
isFlowTemplateModalOpen: boolean;
setIsFlowTemplateModalOpen: (value: boolean) => void;
};
interface DataType {
key: string;
name: string;
age: number;
address: string;
tags: string[];
}
export const FlowTemplateModal: React.FC<Props> = ({ isFlowTemplateModalOpen, setIsFlowTemplateModalOpen }) => {
const { t } = useTranslation();
const [dataSource, setDataSource] = useState([]);
const onTemplateImport = (record: DataType) => {
if (record?.name) {
localStorage.setItem('importFlowData', JSON.stringify(record));
CanvasWrapper();
setIsFlowTemplateModalOpen(false);
}
};
const columns: TableProps<DataType>['columns'] = [
{
title: t('Template_Name'),
dataIndex: 'name',
key: 'name',
},
{
title: t('Template_Action'),
key: 'action',
render: (_, record) => (
<Space size='middle'>
<Button
type='link'
onClick={() => {
onTemplateImport(record);
}}
block
>
{t('Import_From_Template')}
</Button>
</Space>
),
},
];
useEffect(() => {
getFlowTemplates().then(res => {
console.log(res);
setDataSource(res?.data?.data?.items);
});
}, []);
return (
<>
<Modal
title={t('Import_From_Template')}
open={isFlowTemplateModalOpen}
onCancel={() => setIsFlowTemplateModalOpen(false)}
cancelButtonProps={{ className: 'hidden' }}
okButtonProps={{ className: 'hidden' }}
>
<Table dataSource={dataSource} columns={columns} />;
</Modal>
</>
);
};

View File

@ -1,12 +1,12 @@
/* eslint-disable react-hooks/rules-of-hooks */
import { metadataBatch } from '@/client/api';
import { IFlowNodeParameter } from '@/types/flow';
import { convertKeysToCamelCase } from '@/utils/flow';
import { UploadOutlined } from '@ant-design/icons';
import type { UploadProps,UploadFile } from 'antd';
import type { UploadFile, UploadProps } from 'antd';
import { Button, Upload, message } from 'antd';
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { metadataBatch } from '@/client/api';
type Props = {
formValuesChange: any;
@ -17,31 +17,33 @@ export const renderUpload = (params: Props) => {
const { t } = useTranslation();
const urlList = useRef<string[]>([]);
const { data, formValuesChange } = params;
const [fileList, setFileList] = useState<UploadFile[]>([])
const [fileList, setFileList] = useState<UploadFile[]>([]);
// 获取上传文件元数据
useEffect(() => {
useEffect(() => {
if (data.value) {
let uris:string[] = []
typeof(data.value) === 'string'? uris.push(data.value):uris = data.value
let parameter:any = {
uris
}
metadataBatch(parameter).then((res)=>{
let urlList:UploadFile[] = []
for (let index = 0; index < res.data.data.length; index++) {
const element = res.data.data[index];
urlList.push({
let uris: string[] = [];
typeof data.value === 'string' ? uris.push(data.value) : (uris = data.value);
const parameter: any = {
uris,
};
metadataBatch(parameter)
.then(res => {
const urlList: UploadFile[] = [];
for (let index = 0; index < res.data.data.length; index++) {
const element = res.data.data[index];
urlList.push({
uid: element.file_id,
name:element.file_name,
name: element.file_name,
status: 'done',
url: element.uri,
})
}
setFileList(urlList)
}).catch((error)=>{
console.log(error)
})
});
}
setFileList(urlList);
})
.catch(error => {
console.log(error);
});
}
}, []);
@ -102,11 +104,17 @@ export const renderUpload = (params: Props) => {
return (
<div className='p-2 text-sm text-center'>
<Upload onRemove={handleFileRemove} {...props} {...attr} multiple={data.is_list?true:false} accept={uploadType}>
<Button loading={uploading} icon={<UploadOutlined />}>
{t('Upload_Data')}
</Button>
</Upload>
<Upload
onRemove={handleFileRemove}
{...props}
{...attr}
multiple={data.is_list ? true : false}
accept={uploadType}
>
<Button loading={uploading} icon={<UploadOutlined />}>
{t('Upload_Data')}
</Button>
</Upload>
</div>
);
};

View File

@ -21,4 +21,7 @@ export const FlowEn = {
Add_Parameter: 'Add Parameter',
Higher_Order_Nodes: 'Higher Order',
All_Nodes: 'All',
Import_From_Template: 'Import from template',
Template_Name: 'Template Name',
Template_Action: 'Action',
};

View File

@ -21,4 +21,7 @@ export const FlowZn = {
Add_Parameter: '添加参数',
Higher_Order_Nodes: '高阶',
All_Nodes: '所有',
Import_Template: '从模版导入',
Template_Name: '模版名称',
Template_Action: '操作',
};

View File

@ -2,11 +2,17 @@ import { apiInterceptors, getFlowById } from '@/client/api';
import MuiLoading from '@/components/common/loading';
import AddNodesSider from '@/components/flow/add-nodes-sider';
import ButtonEdge from '@/components/flow/button-edge';
import { AddFlowVariableModal, ExportFlowModal, ImportFlowModal, SaveFlowModal } from '@/components/flow/canvas-modal';
import {
AddFlowVariableModal,
ExportFlowModal,
FlowTemplateModal,
ImportFlowModal,
SaveFlowModal,
} from '@/components/flow/canvas-modal';
import CanvasNode from '@/components/flow/canvas-node';
import { IFlowData, IFlowUpdateParam } from '@/types/flow';
import { checkFlowDataRequied, getUniqueNodeId, mapUnderlineToHump } from '@/utils/flow';
import { ExportOutlined, FrownOutlined, ImportOutlined, SaveOutlined } from '@ant-design/icons';
import { ExportOutlined, FileAddOutlined, FrownOutlined, ImportOutlined, SaveOutlined } from '@ant-design/icons';
import { Divider, Space, Tooltip, message, notification } from 'antd';
import { useSearchParams } from 'next/navigation';
import React, { DragEvent, useCallback, useEffect, useRef, useState } from 'react';
@ -43,6 +49,7 @@ const Canvas: React.FC = () => {
const [isSaveFlowModalOpen, setIsSaveFlowModalOpen] = useState(false);
const [isExportFlowModalOpen, setIsExportFlowModalOpen] = useState(false);
const [isImportModalOpen, setIsImportFlowModalOpen] = useState(false);
const [isFlowTemplateModalOpen, setIsFlowTemplateModalOpen] = useState(false);
if (localStorage.getItem('importFlowData')) {
const importFlowData = JSON.parse(localStorage.getItem('importFlowData') || '');
@ -191,19 +198,15 @@ const Canvas: React.FC = () => {
setIsSaveFlowModalOpen(true);
}
function onExport() {
setIsExportFlowModalOpen(true);
}
function onImport() {
setIsImportFlowModalOpen(true);
}
const getButtonList = () => {
const buttonList = [
{
title: t('template'),
icon: <FileAddOutlined className='block text-xl' onClick={() => setIsFlowTemplateModalOpen(true)} />,
},
{
title: t('Import'),
icon: <ImportOutlined className='block text-xl' onClick={onImport} />,
icon: <ImportOutlined className='block text-xl' onClick={() => setIsImportFlowModalOpen(true)} />,
},
{
title: t('save'),
@ -214,7 +217,7 @@ const Canvas: React.FC = () => {
if (id !== '') {
buttonList.unshift({
title: t('Export'),
icon: <ExportOutlined className='block text-xl' onClick={onExport} />,
icon: <ExportOutlined className='block text-xl' onClick={() => setIsExportFlowModalOpen(true)} />,
});
}
@ -290,6 +293,11 @@ const Canvas: React.FC = () => {
setIsImportFlowModalOpen={setIsImportFlowModalOpen}
/>
<FlowTemplateModal
isFlowTemplateModalOpen={isFlowTemplateModalOpen}
setIsFlowTemplateModalOpen={setIsFlowTemplateModalOpen}
/>
{contextHolder}
</>
);