chore: import upstream snapshot with attribution
Update draft releases / main (push) Has been cancelled
Build and push docs image / build-image (push) Has been cancelled
Build Web Application / build-web (macos-latest) (push) Has been cancelled
Build Web Application / build-web (ubuntu-latest) (push) Has been cancelled
Python Code Quality Checks / build (push) Has been cancelled
Test Python / test-python (macos-latest, 3.10) (push) Has been cancelled
Test Python / test-python (macos-latest, 3.11) (push) Has been cancelled
Test Python / test-python (ubuntu-latest, 3.10) (push) Has been cancelled
Test Python / test-python (ubuntu-latest, 3.11) (push) Has been cancelled
Update draft releases / main (push) Has been cancelled
Build and push docs image / build-image (push) Has been cancelled
Build Web Application / build-web (macos-latest) (push) Has been cancelled
Build Web Application / build-web (ubuntu-latest) (push) Has been cancelled
Python Code Quality Checks / build (push) Has been cancelled
Test Python / test-python (macos-latest, 3.10) (push) Has been cancelled
Test Python / test-python (macos-latest, 3.11) (push) Has been cancelled
Test Python / test-python (ubuntu-latest, 3.10) (push) Has been cancelled
Test Python / test-python (ubuntu-latest, 3.11) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Cascader } from 'antd';
|
||||
|
||||
export const renderCascader = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return <Cascader {...attr} options={data.options} placeholder='please select' className='w-full nodrag' />;
|
||||
};
|
||||
@@ -0,0 +1,15 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Checkbox } from 'antd';
|
||||
|
||||
export const renderCheckbox = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return (
|
||||
data.options?.length > 0 && (
|
||||
<div className='bg-white p-2 rounded'>
|
||||
<Checkbox.Group {...attr} options={data.options} />
|
||||
</div>
|
||||
)
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import Editor from '@monaco-editor/react';
|
||||
import { Button, Form, Modal } from 'antd';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
export const renderCodeEditor = (data: IFlowNodeParameter) => {
|
||||
const { t } = useTranslation();
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const showModal = () => {
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
|
||||
const onOk = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
||||
const onCancel = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
|
||||
const modalWidth = useMemo(() => {
|
||||
if (data?.ui?.editor?.width) {
|
||||
return data?.ui?.editor?.width + 100;
|
||||
}
|
||||
return '80%';
|
||||
}, [data?.ui?.editor?.width]);
|
||||
|
||||
return (
|
||||
<div className='p-2 text-sm'>
|
||||
<Button type='default' onClick={showModal}>
|
||||
{t('Open_Code_Editor')}
|
||||
</Button>
|
||||
|
||||
<Modal title={t('Code_Editor')} width={modalWidth} open={isModalOpen} onOk={onOk} onCancel={onCancel}>
|
||||
<Form.Item name={data?.name}>
|
||||
<Editor
|
||||
{...attr}
|
||||
width={data?.ui?.editor?.width || '100%'}
|
||||
height={data?.ui?.editor?.height || 200}
|
||||
defaultLanguage={data?.ui?.language}
|
||||
theme='vs-dark'
|
||||
options={{
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
wordWrap: 'on',
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import type { DatePickerProps } from 'antd';
|
||||
import { DatePicker } from 'antd';
|
||||
|
||||
type Props = {
|
||||
formValuesChange: any;
|
||||
data: IFlowNodeParameter;
|
||||
onChange?: (value: any) => void;
|
||||
};
|
||||
export const renderDatePicker = (params: Props) => {
|
||||
const { data, formValuesChange } = params;
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
const onChange: DatePickerProps['onChange'] = (_, dateString) => {
|
||||
formValuesChange({
|
||||
[data.name]: dateString,
|
||||
});
|
||||
};
|
||||
|
||||
return <DatePicker onChange={onChange} {...attr} className='w-full' placeholder='please select a date' />;
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
export * from './cascader';
|
||||
export * from './checkbox';
|
||||
export * from './code-editor';
|
||||
export * from './date-picker';
|
||||
export * from './input';
|
||||
export * from './password';
|
||||
export * from './radio';
|
||||
export * from './select';
|
||||
export * from './slider';
|
||||
export * from './textarea';
|
||||
export * from './time-picker';
|
||||
export * from './tree-select';
|
||||
export * from './upload';
|
||||
export * from './variables';
|
||||
@@ -0,0 +1,21 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import * as Icons from '@ant-design/icons';
|
||||
import { Input } from 'antd';
|
||||
|
||||
const getIconComponent = (iconString: string) => {
|
||||
const match = iconString.match(/^icon:(\w+)$/);
|
||||
if (match) {
|
||||
const iconName = match[1] as keyof typeof Icons;
|
||||
const IconComponent = Icons[iconName];
|
||||
return IconComponent ? <IconComponent /> : null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
export const renderInput = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
attr.prefix = getIconComponent(data.ui?.attr?.prefix || '');
|
||||
|
||||
return <Input {...attr} className='w-full' placeholder='please input' allowClear />;
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Input } from 'antd';
|
||||
|
||||
const { Password } = Input;
|
||||
|
||||
export const renderPassword = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return <Password {...attr} placeholder='input password' />;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Radio } from 'antd';
|
||||
|
||||
export const renderRadio = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return (
|
||||
<div className='bg-white p-2 rounded'>
|
||||
<Radio.Group {...attr} options={data.options} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Select } from 'antd';
|
||||
|
||||
export const renderSelect = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data?.ui?.attr || {});
|
||||
|
||||
return <Select {...attr} className='w-full nodrag' placeholder='please select' options={data.options} />;
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Slider } from 'antd';
|
||||
|
||||
export const renderSlider = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return (
|
||||
<>
|
||||
{data.is_list ? <Slider range className='mt-8 nodrag' {...attr} /> : <Slider className='mt-8 nodrag' {...attr} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Input } from 'antd';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
export const renderTextArea = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
return <TextArea className='nowheel mb-3 nodrag' {...attr} />;
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import type { TimePickerProps } from 'antd';
|
||||
import { TimePicker } from 'antd';
|
||||
|
||||
type Props = {
|
||||
formValuesChange: any;
|
||||
data: IFlowNodeParameter;
|
||||
};
|
||||
export const renderTimePicker = (params: Props) => {
|
||||
const { data, formValuesChange } = params;
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
const onChangeTime: TimePickerProps['onChange'] = (_, timeString) => {
|
||||
formValuesChange(
|
||||
{
|
||||
time: timeString,
|
||||
},
|
||||
{ force: true },
|
||||
);
|
||||
};
|
||||
|
||||
return <TimePicker {...attr} onChange={onChangeTime} className='w-full' placeholder='please select a moment' />;
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { TreeSelect } from 'antd';
|
||||
|
||||
export const renderTreeSelect = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return <TreeSelect {...attr} className='w-full nodrag' treeDefaultExpandAll treeData={data.options} />;
|
||||
};
|
||||
@@ -0,0 +1,120 @@
|
||||
/* 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 { UploadFile, UploadProps } from 'antd';
|
||||
import { Button, Upload, message } from 'antd';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
type Props = {
|
||||
formValuesChange: any;
|
||||
data: IFlowNodeParameter;
|
||||
onChange?: (value: any) => void;
|
||||
};
|
||||
export const renderUpload = (params: Props) => {
|
||||
const { t } = useTranslation();
|
||||
const urlList = useRef<string[]>([]);
|
||||
const { data, formValuesChange } = params;
|
||||
const [fileList, setFileList] = useState<UploadFile[]>([]);
|
||||
|
||||
// 获取上传文件元数据
|
||||
useEffect(() => {
|
||||
if (data.value) {
|
||||
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,
|
||||
status: 'done',
|
||||
url: element.uri,
|
||||
});
|
||||
}
|
||||
setFileList(urlList);
|
||||
})
|
||||
.catch(error => {
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
}, []);
|
||||
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [uploadType, setUploadType] = useState('');
|
||||
const getUploadSuccessUrl = (url: string) => {
|
||||
if (urlList.current.length === data.ui.attr.max_count) {
|
||||
urlList.current.pop();
|
||||
}
|
||||
urlList.current.push(url);
|
||||
if (data.ui.attr.max_count === 1) {
|
||||
formValuesChange({ [data.name]: urlList.current.toString() });
|
||||
} else {
|
||||
formValuesChange({ [data.name]: urlList.current });
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileRemove = (file: any) => {
|
||||
const index = urlList.current.indexOf(file.response.data[0].uri);
|
||||
if (index !== -1) {
|
||||
urlList.current.splice(index, 1);
|
||||
}
|
||||
setUploading(false);
|
||||
if (data.ui.attr.max_count === 1) {
|
||||
formValuesChange({ [data.name]: urlList.current.toString() });
|
||||
} else {
|
||||
formValuesChange({ [data.name]: urlList.current });
|
||||
}
|
||||
};
|
||||
|
||||
const props: UploadProps = {
|
||||
name: 'files',
|
||||
action: process.env.API_BASE_URL ?? '' + data.ui.action,
|
||||
headers: {
|
||||
authorization: 'authorization-text',
|
||||
},
|
||||
defaultFileList: fileList,
|
||||
onChange(info) {
|
||||
setUploading(true);
|
||||
if (info.file.status !== 'uploading') {
|
||||
setUploading(false);
|
||||
}
|
||||
if (info.file.status === 'done') {
|
||||
setUploading(false);
|
||||
message.success(`${info.file.response.data[0].file_name} ${t('Upload_Data_Successfully')}`);
|
||||
getUploadSuccessUrl(info.file.response.data[0].uri);
|
||||
} else if (info.file.status === 'error') {
|
||||
setUploading(false);
|
||||
message.error(`${info.file.response.data[0].file_name} ${t('Upload_Data_Failed')}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if (!uploadType && data.ui?.file_types && Array.isArray(data.ui?.file_types)) {
|
||||
setUploadType(data.ui?.file_types.toString());
|
||||
}
|
||||
|
||||
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>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Input } from 'antd';
|
||||
|
||||
export const renderVariables = (data: IFlowNodeParameter) => {
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return <Input {...attr} className='w-full' placeholder='please input' allowClear />;
|
||||
};
|
||||
Reference in New Issue
Block a user