mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-11 22:09:44 +00:00
feat: add atom components to flow(select、input、checkbox、dataPicker) (#1818)
This commit is contained in:
@@ -4,8 +4,20 @@ import React from 'react';
|
||||
import RequiredIcon from './required-icon';
|
||||
import NodeHandler from './node-handler';
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { RenderSelect, RenderSlider, RenderTreeSelect, RenderTimePicker, RenderTextArea, RenderCascader } from './node-renderer';
|
||||
import { uiAtrrtUnderlineToHump } from '@/utils/flow';
|
||||
import {
|
||||
RenderSelect,
|
||||
RenderCheckbox,
|
||||
RenderRadio,
|
||||
RenderCascader,
|
||||
RenderDataPicker,
|
||||
RenderInput,
|
||||
RenderSlider,
|
||||
RenderTreeSelect,
|
||||
RenderTimePicker,
|
||||
RenderTextArea,
|
||||
} from './node-renderer';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
|
||||
interface NodeParamHandlerProps {
|
||||
node: IFlowNode;
|
||||
data: IFlowNodeParameter;
|
||||
@@ -22,7 +34,7 @@ const NodeParamHandler: React.FC<NodeParamHandlerProps> = ({ node, data, label,
|
||||
|
||||
// render node parameters based on AWEL1.0
|
||||
function renderNodeWithoutUiParam(data: IFlowNodeParameter) {
|
||||
let defaultValue = data.value !== null && data.value !== undefined ? data.value : data.default;
|
||||
let defaultValue = data.value ?? data.default;
|
||||
|
||||
switch (data.type_name) {
|
||||
case 'int':
|
||||
@@ -102,22 +114,34 @@ const NodeParamHandler: React.FC<NodeParamHandlerProps> = ({ node, data, label,
|
||||
|
||||
// render node parameters based on AWEL2.0
|
||||
function renderNodeWithUiParam(data: IFlowNodeParameter) {
|
||||
let defaultValue = data.value !== null && data.value !== undefined ? data.value : data.default;
|
||||
if (data?.ui?.attr) {
|
||||
uiAtrrtUnderlineToHump(data.ui.attr);
|
||||
}
|
||||
// TODO: 根据ui_type渲染不同的组件
|
||||
let defaultValue = data.value ?? data.default;
|
||||
const props = { data, defaultValue, onChange };
|
||||
|
||||
switch (data?.ui?.ui_type) {
|
||||
case 'select':
|
||||
return <RenderSelect data={data} defaultValue={defaultValue} onChange={onChange} />;
|
||||
return <RenderSelect {...props} />;
|
||||
case 'cascader':
|
||||
return <RenderCascader {...props} />;
|
||||
case 'checkbox':
|
||||
return <RenderCheckbox {...props} />;
|
||||
case 'radio':
|
||||
return <RenderRadio {...props} />;
|
||||
case 'input':
|
||||
return <RenderInput {...props} />;
|
||||
case 'select':
|
||||
return <RenderSelect {...props} />;
|
||||
case 'text_area':
|
||||
return <RenderTextArea data={data} defaultValue={defaultValue} onChange={onChange} />;
|
||||
return <RenderTextArea {...props} />;
|
||||
case 'slider':
|
||||
return <RenderSlider data={data} defaultValue={defaultValue} onChange={onChange} />;
|
||||
return <RenderSlider {...props} />;
|
||||
case 'data_picker':
|
||||
return <RenderDataPicker {...props} />;
|
||||
case 'time_picker':
|
||||
return <RenderTimePicker data={data} defaultValue={defaultValue} onChange={onChange} />;
|
||||
return <RenderTimePicker {...props} />;
|
||||
case 'tree_select':
|
||||
return <RenderTreeSelect data={data} defaultValue={defaultValue} onChange={onChange} />;
|
||||
return <RenderTreeSelect {...props} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,3 +1,25 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Cascader } from 'antd';
|
||||
|
||||
export const RenderCascader = (props: IFlowNodeParameter) => {};
|
||||
type Props = {
|
||||
data: IFlowNodeParameter;
|
||||
defaultValue: any;
|
||||
onChange: (value: any) => void;
|
||||
};
|
||||
|
||||
export const RenderCascader = (params: Props) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return (
|
||||
<Cascader
|
||||
{...attr}
|
||||
options={data.options}
|
||||
defaultValue={defaultValue}
|
||||
placeholder="Please select"
|
||||
className="w-full nodrag"
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
26
web/components/flow/node-renderer/checkbox.tsx
Normal file
26
web/components/flow/node-renderer/checkbox.tsx
Normal file
@@ -0,0 +1,26 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Checkbox } from 'antd';
|
||||
|
||||
type Props = {
|
||||
data: IFlowNodeParameter;
|
||||
defaultValue: any;
|
||||
onChange: (value: any) => void;
|
||||
};
|
||||
|
||||
export const RenderCheckbox = (params: Props) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return (
|
||||
data.options?.length > 0 && (
|
||||
<Checkbox.Group
|
||||
{...attr}
|
||||
options={data.options}
|
||||
disabled
|
||||
defaultValue={defaultValue}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
);
|
||||
};
|
17
web/components/flow/node-renderer/data-picker.tsx
Normal file
17
web/components/flow/node-renderer/data-picker.tsx
Normal file
@@ -0,0 +1,17 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { DatePicker } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
type Props = {
|
||||
data: IFlowNodeParameter;
|
||||
defaultValue: any;
|
||||
onChange: (value: any) => void;
|
||||
};
|
||||
|
||||
export const RenderDataPicker = (params: Props) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return <DatePicker {...attr} defaultValue={dayjs(defaultValue)} onChange={onChange} />;
|
||||
};
|
@@ -1,6 +1,10 @@
|
||||
export * from './select';
|
||||
export * from './cascader';
|
||||
export * from './data-picker';
|
||||
export * from './input';
|
||||
export * from './checkbox';
|
||||
export * from './radio';
|
||||
export * from './textarea';
|
||||
export * from './slider';
|
||||
export * from './timePicker';
|
||||
export * from './treeSelect';
|
||||
export * from './time-picker';
|
||||
export * from './tree-select';
|
||||
|
25
web/components/flow/node-renderer/input.tsx
Normal file
25
web/components/flow/node-renderer/input.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Input } from 'antd';
|
||||
|
||||
type Props = {
|
||||
data: IFlowNodeParameter;
|
||||
defaultValue: any;
|
||||
onChange: (value: any) => void;
|
||||
};
|
||||
|
||||
export const RenderInput = (params: Props) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return (
|
||||
<Input
|
||||
{...attr}
|
||||
className="w-full"
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
25
web/components/flow/node-renderer/radio.tsx
Normal file
25
web/components/flow/node-renderer/radio.tsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Radio } from 'antd';
|
||||
|
||||
type Props = {
|
||||
data: IFlowNodeParameter;
|
||||
defaultValue: any;
|
||||
onChange: (value: any) => void;
|
||||
};
|
||||
|
||||
export const RenderRadio = (params: Props) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return (
|
||||
<Radio.Group
|
||||
{...attr}
|
||||
options={data.options}
|
||||
onChange={(e) => {
|
||||
onChange(e.target.checked);
|
||||
}}
|
||||
defaultValue={defaultValue}
|
||||
/>
|
||||
);
|
||||
};
|
@@ -1,23 +1,25 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { Select } from 'antd';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
|
||||
type SelectProps = {
|
||||
type Props = {
|
||||
data: IFlowNodeParameter;
|
||||
defaultValue: any;
|
||||
onChange: (value: any) => void;
|
||||
};
|
||||
|
||||
export const RenderSelect = (params: SelectProps) => {
|
||||
export const RenderSelect = (params: Props) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
return (
|
||||
data.options?.length > 0 && (
|
||||
<Select
|
||||
className="w-full nodrag"
|
||||
defaultValue={defaultValue}
|
||||
options={data.options.map((item: any) => ({ label: item.label, value: item.value }))}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)
|
||||
<Select
|
||||
{...attr}
|
||||
className="w-full nodrag"
|
||||
placeholder="Please select"
|
||||
defaultValue={defaultValue}
|
||||
options={data.options}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@@ -1,4 +1,5 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Col, InputNumber, Row, Slider, Space } from 'antd';
|
||||
import type { InputNumberProps } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
@@ -11,25 +12,27 @@ type TextAreaProps = {
|
||||
|
||||
export const RenderSlider = (params: TextAreaProps) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
const [inputValue, setInputValue] = useState(defaultValue);
|
||||
|
||||
const onChangeSlider: InputNumberProps['onChange'] = (newValue) => {
|
||||
setInputValue(newValue as number);
|
||||
onChange(newValue as number);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{data?.ui?.show_input ? (
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Slider {...data.ui.attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<InputNumber {...data.ui.attr} style={{ margin: '0 16px' }} value={inputValue} onChange={onChangeSlider} />
|
||||
<InputNumber {...attr} style={{ margin: '0 16px' }} value={inputValue} onChange={onChangeSlider} />
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<Slider {...data.ui.attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||
)}
|
||||
</>
|
||||
);
|
@@ -1,4 +1,5 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
import { Col, InputNumber, Row, Slider, Space } from 'antd';
|
||||
import type { InputNumberProps } from 'antd';
|
||||
import React, { useState } from 'react';
|
||||
@@ -11,7 +12,7 @@ type TextAreaProps = {
|
||||
|
||||
export const RenderSlider = (params: TextAreaProps) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
const [inputValue, setInputValue] = useState(defaultValue);
|
||||
|
||||
const onChangeSlider: InputNumberProps['onChange'] = (newValue) => {
|
||||
@@ -24,14 +25,14 @@ export const RenderSlider = (params: TextAreaProps) => {
|
||||
{data?.ui?.show_input ? (
|
||||
<Row>
|
||||
<Col span={12}>
|
||||
<Slider {...data.ui.attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||
</Col>
|
||||
<Col span={4}>
|
||||
<InputNumber {...data.ui.attr} style={{ margin: '0 16px' }} value={inputValue} onChange={onChangeSlider} />
|
||||
<InputNumber {...attr} style={{ margin: '0 16px' }} value={inputValue} onChange={onChangeSlider} />
|
||||
</Col>
|
||||
</Row>
|
||||
) : (
|
||||
<Slider {...data.ui.attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { Input } from 'antd';
|
||||
import { uiAtrrtUnderlineToHump } from '@/utils/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
|
||||
const { TextArea } = Input;
|
||||
|
||||
@@ -12,7 +12,15 @@ type TextAreaProps = {
|
||||
|
||||
export const RenderTextArea = (params: TextAreaProps) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
uiAtrrtUnderlineToHump(data?.ui?.attr?.autosize || {});
|
||||
convertKeysToCamelCase(data?.ui?.attr?.autosize || {});
|
||||
|
||||
return <TextArea {...data.ui.attr} defaultValue={defaultValue} onChange={(e) => onChange(e.target.value)} {...data.ui.autosize} rows={4} />;
|
||||
return (
|
||||
<TextArea
|
||||
{...data.ui.attr}
|
||||
defaultValue={defaultValue}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
{...data.ui.attr.autosize}
|
||||
rows={4}
|
||||
/>
|
||||
)
|
||||
};
|
||||
|
@@ -2,6 +2,7 @@ import React, { useState } from 'react';
|
||||
import type { TimePickerProps } from 'antd';
|
||||
import { TimePicker } from 'antd';
|
||||
import { IFlowNodeParameter } from '@/types/flow';
|
||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||
|
||||
type TextAreaProps = {
|
||||
data: IFlowNodeParameter;
|
||||
@@ -11,6 +12,7 @@ type TextAreaProps = {
|
||||
export const RenderTimePicker = (params: TextAreaProps) => {
|
||||
const { data, defaultValue, onChange } = params;
|
||||
const [value, setValue] = useState(defaultValue);
|
||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||
|
||||
// dayjs.extend(customParseFormat);
|
||||
|
||||
@@ -19,5 +21,5 @@ export const RenderTimePicker = (params: TextAreaProps) => {
|
||||
setValue(time);
|
||||
};
|
||||
|
||||
return <TimePicker style={{ width: '100%' }} {...data.ui.attr} value={value} onChange={onChangeTime} />;
|
||||
return <TimePicker {...attr} style={{ width: '100%' }} value={value} onChange={onChangeTime} />;
|
||||
};
|
@@ -32,6 +32,7 @@
|
||||
"copy-to-clipboard": "^3.3.3",
|
||||
"cytoscape": "^3.29.2",
|
||||
"cytoscape-euler": "^1.2.2",
|
||||
"dayjs": "^1.11.12",
|
||||
"i18next": "^23.4.5",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.29.4",
|
||||
|
@@ -10,26 +10,7 @@ export const getUniqueNodeId = (nodeData: IFlowNode, nodes: Node[]) => {
|
||||
});
|
||||
return `${nodeData.id}_${count}`;
|
||||
};
|
||||
/**
|
||||
*
|
||||
* 下划线转驼峰
|
||||
*/
|
||||
export const uiAtrrtUnderlineToHump = (obj: any) => {
|
||||
for(let key in obj){
|
||||
if(key.indexOf('_') !== -1){
|
||||
let newKey = key.replace(/_(\w)/g, (all, letter) => {
|
||||
return letter.toUpperCase();
|
||||
});
|
||||
|
||||
obj[newKey] = obj[key];
|
||||
delete obj[key];
|
||||
// 判断是否为对象
|
||||
if(typeof obj[newKey] === 'object'){
|
||||
uiAtrrtUnderlineToHump(obj[newKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 驼峰转下划线,接口协议字段命名规范
|
||||
export const mapHumpToUnderline = (flowData: IFlowData) => {
|
||||
@@ -118,3 +99,31 @@ export const checkFlowDataRequied = (flowData: IFlowData) => {
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const convertKeysToCamelCase = (obj: Record<string, any>): Record<string, any> => {
|
||||
function toCamelCase(str: string): string {
|
||||
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
||||
}
|
||||
|
||||
function isObject(value: any): boolean {
|
||||
return value && typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function convert(obj: any): any {
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map(item => convert(item));
|
||||
} else if (isObject(obj)) {
|
||||
const newObj: Record<string, any> = {};
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
const newKey = toCamelCase(key);
|
||||
newObj[newKey] = convert(obj[key]);
|
||||
}
|
||||
}
|
||||
return newObj;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
return convert(obj);
|
||||
};
|
@@ -2518,6 +2518,11 @@ dayjs@^1.11.11, dayjs@^1.9.1:
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.11.tgz#dfe0e9d54c5f8b68ccf8ca5f72ac603e7e5ed59e"
|
||||
integrity sha512-okzr3f11N6WuqYtZSvm+F776mB41wRZMhKP+hc34YdW+KmtYYK9iqvHSwo2k9FEH3fhGXvOPV6yz2IcSrfRUDg==
|
||||
|
||||
dayjs@^1.11.12:
|
||||
version "1.11.12"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.12.tgz#5245226cc7f40a15bf52e0b99fd2a04669ccac1d"
|
||||
integrity sha512-Rt2g+nTbLlDWZTwwrIXjy9MeiZmSDI375FvZs72ngxx8PDC6YXOeR3q5LAuPzjZQxhiWdRKac7RKV+YyQYfYIg==
|
||||
|
||||
debug@^3.2.7:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
|
||||
|
Reference in New Issue
Block a user