mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-11 22:09:44 +00:00
Merge branch 'feat/sprint-web-flow' into feat/tags
This commit is contained in:
@@ -2,6 +2,9 @@ import i18n from 'i18next';
|
|||||||
import { initReactI18next } from 'react-i18next';
|
import { initReactI18next } from 'react-i18next';
|
||||||
|
|
||||||
const en = {
|
const en = {
|
||||||
|
UploadData: 'Upload Data',
|
||||||
|
CodeEditor: 'Code Editor:',
|
||||||
|
openCodeEditor:'Open Code Editor',
|
||||||
Knowledge_Space: 'Knowledge',
|
Knowledge_Space: 'Knowledge',
|
||||||
space: 'space',
|
space: 'space',
|
||||||
Vector: 'Vector',
|
Vector: 'Vector',
|
||||||
@@ -234,6 +237,9 @@ export interface Resources {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const zh: Resources['translation'] = {
|
const zh: Resources['translation'] = {
|
||||||
|
UploadData: '上传数据',
|
||||||
|
CodeEditor: '代码编辑:',
|
||||||
|
openCodeEditor: '打开代码编辑器',
|
||||||
Knowledge_Space: '知识库',
|
Knowledge_Space: '知识库',
|
||||||
space: '知识库',
|
space: '知识库',
|
||||||
Vector: '向量',
|
Vector: '向量',
|
||||||
|
@@ -15,10 +15,11 @@ import {
|
|||||||
RenderTreeSelect,
|
RenderTreeSelect,
|
||||||
RenderTimePicker,
|
RenderTimePicker,
|
||||||
RenderTextArea,
|
RenderTextArea,
|
||||||
|
RenderUpload,
|
||||||
|
RenderCodeEditor,
|
||||||
RenderPassword,
|
RenderPassword,
|
||||||
RenderVariables,
|
RenderVariables,
|
||||||
} from './node-renderer';
|
} from './node-renderer';
|
||||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
|
||||||
|
|
||||||
interface NodeParamHandlerProps {
|
interface NodeParamHandlerProps {
|
||||||
node: IFlowNode;
|
node: IFlowNode;
|
||||||
@@ -140,11 +141,13 @@ const NodeParamHandler: React.FC<NodeParamHandlerProps> = ({ node, data, label,
|
|||||||
case 'time_picker':
|
case 'time_picker':
|
||||||
return <RenderTimePicker {...props} />;
|
return <RenderTimePicker {...props} />;
|
||||||
case 'tree_select':
|
case 'tree_select':
|
||||||
return <RenderPassword {...props} />;
|
|
||||||
case 'password':
|
|
||||||
return <RenderTreeSelect {...props} />;
|
return <RenderTreeSelect {...props} />;
|
||||||
case 'variables':
|
case 'password':
|
||||||
return <RenderVariables {...props} />;
|
return <RenderPassword {...props} />;
|
||||||
|
case 'upload':
|
||||||
|
return <RenderUpload {...props} />;
|
||||||
|
case 'code_editor':
|
||||||
|
return <RenderCodeEditor {...props} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
67
web/components/flow/node-renderer/codeEditor.tsx
Normal file
67
web/components/flow/node-renderer/codeEditor.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import React, { useState, useMemo } from 'react';
|
||||||
|
import { Button, Modal } from 'antd';
|
||||||
|
import Editor from '@monaco-editor/react';
|
||||||
|
import { IFlowNodeParameter } from '@/types/flow';
|
||||||
|
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: IFlowNodeParameter;
|
||||||
|
defaultValue: any;
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RenderCodeEditor = (params: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { data, defaultValue, onChange } = params;
|
||||||
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const showModal = () => {
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOk = () => {
|
||||||
|
setIsModalOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
setIsModalOpen(false);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 设置弹窗宽度
|
||||||
|
*/
|
||||||
|
const modalWidth = useMemo(() => {
|
||||||
|
if (data?.ui?.editor?.width) {
|
||||||
|
return data?.ui?.editor?.width + 100
|
||||||
|
}
|
||||||
|
return '80%';
|
||||||
|
}, [data?.ui?.editor?.width]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ textAlign: 'center' }} className="p-2 text-sm">
|
||||||
|
<Button type="primary" onClick={showModal}>
|
||||||
|
{t('openCodeEditor')}
|
||||||
|
</Button>
|
||||||
|
<Modal title={t('openCodeEditor')} width={modalWidth} open={isModalOpen} onOk={handleOk} onCancel={handleCancel}>
|
||||||
|
<Editor
|
||||||
|
{...data?.ui?.attr}
|
||||||
|
width={data?.ui?.editor?.width || '100%'}
|
||||||
|
value={defaultValue}
|
||||||
|
style={{ padding: '10px' }}
|
||||||
|
height={data?.ui?.editor?.height || 200}
|
||||||
|
defaultLanguage={data?.ui?.language}
|
||||||
|
onChange={onChange}
|
||||||
|
theme='vs-dark'
|
||||||
|
options={{
|
||||||
|
minimap: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
wordWrap: 'on',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@@ -11,7 +11,6 @@ type Props = {
|
|||||||
|
|
||||||
export const RenderDatePicker = (params: Props) => {
|
export const RenderDatePicker = (params: Props) => {
|
||||||
const { data, defaultValue, onChange } = params;
|
const { data, defaultValue, onChange } = params;
|
||||||
console.log('data', data);
|
|
||||||
|
|
||||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
|
@@ -8,5 +8,7 @@ export * from './textarea';
|
|||||||
export * from './slider';
|
export * from './slider';
|
||||||
export * from './time-picker';
|
export * from './time-picker';
|
||||||
export * from './tree-select';
|
export * from './tree-select';
|
||||||
|
export * from './codeEditor';
|
||||||
|
export * from './upload';
|
||||||
export * from './password';
|
export * from './password';
|
||||||
export * from './variables';
|
export * from './variables';
|
||||||
|
@@ -25,14 +25,14 @@ export const RenderSlider = (params: TextAreaProps) => {
|
|||||||
{data?.ui?.show_input ? (
|
{data?.ui?.show_input ? (
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
<Slider className="w-full nodrag" {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={4}>
|
<Col span={4}>
|
||||||
<InputNumber {...attr} style={{ margin: '0 16px' }} value={inputValue} onChange={onChangeSlider} />
|
<InputNumber {...attr} style={{ margin: '0 16px' }} value={inputValue} onChange={onChangeSlider} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
) : (
|
) : (
|
||||||
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
<Slider className="w-full nodrag" {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@@ -13,6 +13,7 @@ type TextAreaProps = {
|
|||||||
|
|
||||||
export const RenderTextArea = (params: TextAreaProps) => {
|
export const RenderTextArea = (params: TextAreaProps) => {
|
||||||
const { data, defaultValue, onChange } = params;
|
const { data, defaultValue, onChange } = params;
|
||||||
|
|
||||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -1,107 +1,29 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { TreeSelect } from 'antd';
|
import { TreeSelect } from 'antd';
|
||||||
import type { TreeSelectProps } from 'antd';
|
|
||||||
import { IFlowNodeParameter } from '@/types/flow';
|
import { IFlowNodeParameter } from '@/types/flow';
|
||||||
import { Label } from '@mui/icons-material';
|
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||||
|
|
||||||
type TextAreaProps = {
|
type TextAreaProps = {
|
||||||
data: IFlowNodeParameter;
|
data: IFlowNodeParameter;
|
||||||
defaultValue: any;
|
defaultValue: any;
|
||||||
onChange: (value: any) => void;
|
onChange: (value: any) => void;
|
||||||
};
|
};
|
||||||
const treeData = [
|
|
||||||
{
|
|
||||||
value: 'parent 1',
|
|
||||||
title: 'parent 1',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
value: 'parent 1-0',
|
|
||||||
title: 'parent 1-0',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
value: 'leaf1',
|
|
||||||
title: 'leaf1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'leaf2',
|
|
||||||
title: 'leaf2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'leaf3',
|
|
||||||
title: 'leaf3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'leaf4',
|
|
||||||
title: 'leaf4',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'leaf5',
|
|
||||||
title: 'leaf5',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'leaf6',
|
|
||||||
title: 'leaf6',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
value: 'parent 1-1',
|
|
||||||
title: 'parent 1-1',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
value: 'leaf11',
|
|
||||||
title: <b style={{ color: '#08c' }}>leaf11</b>,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
export const RenderTreeSelect = (params: TextAreaProps) => {
|
export const RenderTreeSelect = (params: TextAreaProps) => {
|
||||||
const { data, defaultValue, onChange } = params;
|
const { data, defaultValue, onChange } = params;
|
||||||
// console.log(data.options);
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
// const [value, setValue] = useState<string>();
|
|
||||||
|
|
||||||
// const onChange = (newValue: string) => {
|
|
||||||
// setValue(newValue);
|
|
||||||
// };
|
|
||||||
const [dropdownVisible, setDropdownVisible] = useState(false);
|
|
||||||
|
|
||||||
const handleDropdownVisibleChange = (visible: boolean | ((prevState: boolean) => boolean)) => {
|
|
||||||
setDropdownVisible(visible);
|
|
||||||
|
|
||||||
// 你可以在这里执行更多的逻辑,比如发送请求、更新状态等
|
|
||||||
console.log('Dropdown is now:', visible ? 'visible' : 'hidden');
|
|
||||||
};
|
|
||||||
|
|
||||||
const focus = () => {
|
|
||||||
// console.log('focus==========');
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TreeSelect
|
<div className="p-2 text-sm">
|
||||||
fieldNames={{ label: 'label', value: 'value', children: 'children' }}
|
<TreeSelect
|
||||||
{...data.ui.attr}
|
className="w-full nodrag"
|
||||||
style={{ width: '100%' }}
|
fieldNames={{ label: 'label', value: 'value', children: 'children' }}
|
||||||
value={defaultValue}
|
{...attr}
|
||||||
treeDefaultExpandAll
|
style={{ width: '100%' }}
|
||||||
onChange={onChange}
|
value={defaultValue}
|
||||||
treeData={data.options}
|
treeDefaultExpandAll
|
||||||
onDropdownVisibleChange={handleDropdownVisibleChange}
|
onChange={onChange}
|
||||||
/>
|
treeData={data.options}
|
||||||
|
/>
|
||||||
// TODO: Implement the TreeSelect component
|
</div>
|
||||||
// <TreeSelect
|
|
||||||
// showSearch
|
|
||||||
// style={{ width: '100%' }}
|
|
||||||
// value={value}
|
|
||||||
// dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
|
|
||||||
// placeholder="Please select"
|
|
||||||
// allowClear
|
|
||||||
// treeDefaultExpandAll
|
|
||||||
// onChange={onChange}
|
|
||||||
// treeData={treeData}
|
|
||||||
// getPopupContainer={() => document.body}
|
|
||||||
// />
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
40
web/components/flow/node-renderer/upload.tsx
Normal file
40
web/components/flow/node-renderer/upload.tsx
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
|
import type { UploadProps } from 'antd';
|
||||||
|
import { Button, message, Upload } from 'antd';
|
||||||
|
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||||
|
import { IFlowNodeParameter } from '@/types/flow';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
|
||||||
|
const props: UploadProps = {
|
||||||
|
name: 'file',
|
||||||
|
action: 'https://660d2bd96ddfa2943b33731c.mockapi.io/api/upload',
|
||||||
|
headers: {
|
||||||
|
authorization: 'authorization-text',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
data: IFlowNodeParameter;
|
||||||
|
defaultValue: any;
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RenderUpload = (params: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { data, defaultValue, onChange } = params;
|
||||||
|
|
||||||
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ textAlign: 'center' }} className="p-2 text-sm">
|
||||||
|
<Upload {...attr} {...props}>
|
||||||
|
<Button icon={<UploadOutlined />}>{t('UploadData')}</Button>
|
||||||
|
</Upload>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@@ -54,10 +54,15 @@ export type IFlowNodeParameter = {
|
|||||||
|
|
||||||
export type IFlowNodeParameterUI = {
|
export type IFlowNodeParameterUI = {
|
||||||
ui_type: string;
|
ui_type: string;
|
||||||
|
language: string;
|
||||||
attr: {
|
attr: {
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
editor: {
|
||||||
|
width: Number;
|
||||||
|
height: Number;
|
||||||
|
};
|
||||||
show_input: boolean;
|
show_input: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import axios from './ctx-axios';
|
import axios from './ctx-axios';
|
||||||
import { isPlainObject } from 'lodash';
|
import { isPlainObject } from 'lodash';
|
||||||
|
|
||||||
const DEFAULT_HEADERS = {
|
const DEFAULT_HEADERS = {
|
||||||
'content-type': 'application/json',
|
'content-type': 'application/json',
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user