mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-09-14 13:40:54 +00:00
Merge branch 'feat/sprint-web-flow' into feat-sprint-web-flow-2.0
This commit is contained in:
@@ -11,6 +11,7 @@ _UI_TYPE = Literal[
|
|||||||
"select",
|
"select",
|
||||||
"cascader",
|
"cascader",
|
||||||
"checkbox",
|
"checkbox",
|
||||||
|
"radio",
|
||||||
"date_picker",
|
"date_picker",
|
||||||
"input",
|
"input",
|
||||||
"text_area",
|
"text_area",
|
||||||
@@ -175,6 +176,12 @@ class UICheckbox(UIComponent):
|
|||||||
self._check_options(parameter_dict.get("options", {}))
|
self._check_options(parameter_dict.get("options", {}))
|
||||||
|
|
||||||
|
|
||||||
|
class UIRadio(UICheckbox):
|
||||||
|
"""Radio component."""
|
||||||
|
|
||||||
|
ui_type: Literal["radio"] = Field("radio", frozen=True) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
class UIDatePicker(UIComponent):
|
class UIDatePicker(UIComponent):
|
||||||
"""Date picker component."""
|
"""Date picker component."""
|
||||||
|
|
||||||
@@ -232,23 +239,31 @@ class UIInput(UIComponent):
|
|||||||
class UITextArea(PanelEditorMixin, UIInput):
|
class UITextArea(PanelEditorMixin, UIInput):
|
||||||
"""Text area component."""
|
"""Text area component."""
|
||||||
|
|
||||||
class AutoSize(BaseModel):
|
class UIAttribute(UIInput.UIAttribute):
|
||||||
"""Auto size configuration."""
|
"""Text area attribute."""
|
||||||
|
|
||||||
min_rows: Optional[int] = Field(
|
class AutoSize(BaseModel):
|
||||||
|
"""Auto size configuration."""
|
||||||
|
|
||||||
|
min_rows: Optional[int] = Field(
|
||||||
|
None,
|
||||||
|
description="The minimum number of rows",
|
||||||
|
)
|
||||||
|
max_rows: Optional[int] = Field(
|
||||||
|
None,
|
||||||
|
description="The maximum number of rows",
|
||||||
|
)
|
||||||
|
|
||||||
|
auto_size: Optional[Union[bool, AutoSize]] = Field(
|
||||||
None,
|
None,
|
||||||
description="The minimum number of rows",
|
description="Whether the height of the textarea automatically adjusts "
|
||||||
)
|
"based on the content",
|
||||||
max_rows: Optional[int] = Field(
|
|
||||||
None,
|
|
||||||
description="The maximum number of rows",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
ui_type: Literal["text_area"] = Field("text_area", frozen=True) # type: ignore
|
ui_type: Literal["text_area"] = Field("text_area", frozen=True) # type: ignore
|
||||||
autosize: Optional[Union[bool, AutoSize]] = Field(
|
attr: Optional[UIAttribute] = Field(
|
||||||
None,
|
None,
|
||||||
description="Whether the height of the textarea automatically adjusts based "
|
description="The attributes of the component",
|
||||||
"on the content",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -430,8 +445,9 @@ class UICodeEditor(UITextArea):
|
|||||||
class DefaultUITextArea(UITextArea):
|
class DefaultUITextArea(UITextArea):
|
||||||
"""Default text area component."""
|
"""Default text area component."""
|
||||||
|
|
||||||
autosize: Union[bool, UITextArea.AutoSize] = Field(
|
attr: Optional[UITextArea.UIAttribute] = Field(
|
||||||
default_factory=lambda: UITextArea.AutoSize(min_rows=2, max_rows=40),
|
default_factory=lambda: UITextArea.UIAttribute(
|
||||||
description="Whether the height of the textarea automatically adjusts based "
|
auto_size=UITextArea.UIAttribute.AutoSize(min_rows=2, max_rows=40)
|
||||||
"on the content",
|
),
|
||||||
|
description="The attributes of the component",
|
||||||
)
|
)
|
||||||
|
@@ -1,20 +1,12 @@
|
|||||||
import json
|
import json
|
||||||
from functools import cache
|
from functools import cache
|
||||||
from typing import Dict, List, Literal, Optional, Union
|
from typing import Dict, List, Optional, Union
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
from fastapi import APIRouter, Depends, HTTPException, Query, Request
|
||||||
from fastapi.security.http import HTTPAuthorizationCredentials, HTTPBearer
|
from fastapi.security.http import HTTPAuthorizationCredentials, HTTPBearer
|
||||||
|
|
||||||
from dbgpt.component import SystemApp
|
from dbgpt.component import SystemApp
|
||||||
from dbgpt.core.awel.flow import ResourceMetadata, ViewMetadata
|
from dbgpt.core.awel.flow import ResourceMetadata, ViewMetadata
|
||||||
from dbgpt.core.interface.variables import (
|
|
||||||
BUILTIN_VARIABLES_CORE_FLOW_NODES,
|
|
||||||
BUILTIN_VARIABLES_CORE_FLOWS,
|
|
||||||
BUILTIN_VARIABLES_CORE_SECRETS,
|
|
||||||
BUILTIN_VARIABLES_CORE_VARIABLES,
|
|
||||||
BuiltinVariablesProvider,
|
|
||||||
StorageVariables,
|
|
||||||
)
|
|
||||||
from dbgpt.serve.core import Result, blocking_func_to_async
|
from dbgpt.serve.core import Result, blocking_func_to_async
|
||||||
from dbgpt.util import PaginationResult
|
from dbgpt.util import PaginationResult
|
||||||
|
|
||||||
@@ -330,6 +322,12 @@ async def update_variables(
|
|||||||
return Result.succ(res)
|
return Result.succ(res)
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/flow/debug")
|
||||||
|
async def debug():
|
||||||
|
"""Debug the flow."""
|
||||||
|
# TODO: Implement the debug endpoint
|
||||||
|
|
||||||
|
|
||||||
def init_endpoints(system_app: SystemApp) -> None:
|
def init_endpoints(system_app: SystemApp) -> None:
|
||||||
"""Initialize the endpoints"""
|
"""Initialize the endpoints"""
|
||||||
from .variables_provider import (
|
from .variables_provider import (
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
from typing import Any, List, Literal, Optional
|
from typing import Any, Dict, List, Literal, Optional, Union
|
||||||
|
|
||||||
from dbgpt._private.pydantic import BaseModel, ConfigDict, Field
|
from dbgpt._private.pydantic import BaseModel, ConfigDict, Field
|
||||||
|
from dbgpt.core.awel import CommonLLMHttpRequestBody
|
||||||
from dbgpt.core.awel.flow.flow_factory import FlowPanel
|
from dbgpt.core.awel.flow.flow_factory import FlowPanel
|
||||||
from dbgpt.core.awel.util.parameter_util import RefreshOptionRequest
|
from dbgpt.core.awel.util.parameter_util import RefreshOptionRequest
|
||||||
|
|
||||||
@@ -113,3 +114,26 @@ class RefreshNodeRequest(BaseModel):
|
|||||||
title="The refresh options",
|
title="The refresh options",
|
||||||
description="The refresh options",
|
description="The refresh options",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class FlowDebugRequest(BaseModel):
|
||||||
|
"""Flow response model"""
|
||||||
|
|
||||||
|
model_config = ConfigDict(title=f"FlowDebugRequest")
|
||||||
|
flow: ServeRequest = Field(
|
||||||
|
...,
|
||||||
|
title="The flow to debug",
|
||||||
|
description="The flow to debug",
|
||||||
|
)
|
||||||
|
request: Union[CommonLLMHttpRequestBody, Dict[str, Any]] = Field(
|
||||||
|
...,
|
||||||
|
title="The request to debug",
|
||||||
|
description="The request to debug",
|
||||||
|
)
|
||||||
|
variables: Optional[Dict[str, Any]] = Field(
|
||||||
|
None,
|
||||||
|
title="The variables to debug",
|
||||||
|
description="The variables to debug",
|
||||||
|
)
|
||||||
|
user_name: Optional[str] = Field(None, description="User name")
|
||||||
|
sys_code: Optional[str] = Field(None, description="System code")
|
||||||
|
@@ -206,7 +206,7 @@ class ExampleFlowCheckboxOperator(MapOperator[str, str]):
|
|||||||
OptionValue(label="Orange", name="orange", value="orange"),
|
OptionValue(label="Orange", name="orange", value="orange"),
|
||||||
OptionValue(label="Pear", name="pear", value="pear"),
|
OptionValue(label="Pear", name="pear", value="pear"),
|
||||||
],
|
],
|
||||||
ui=ui.UICheckbox(attr=ui.UICheckbox.UIAttribute(show_search=True)),
|
ui=ui.UICheckbox(),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
inputs=[
|
inputs=[
|
||||||
@@ -236,6 +236,59 @@ class ExampleFlowCheckboxOperator(MapOperator[str, str]):
|
|||||||
return "Your name is %s, and you like %s." % (user_name, ", ".join(self.fruits))
|
return "Your name is %s, and you like %s." % (user_name, ", ".join(self.fruits))
|
||||||
|
|
||||||
|
|
||||||
|
class ExampleFlowRadioOperator(MapOperator[str, str]):
|
||||||
|
"""An example flow operator that includes a radio as parameter."""
|
||||||
|
|
||||||
|
metadata = ViewMetadata(
|
||||||
|
label="Example Flow Radio",
|
||||||
|
name="example_flow_radio",
|
||||||
|
category=OperatorCategory.EXAMPLE,
|
||||||
|
description="An example flow operator that includes a radio as parameter.",
|
||||||
|
parameters=[
|
||||||
|
Parameter.build_from(
|
||||||
|
"Fruits Selector",
|
||||||
|
"fruits",
|
||||||
|
type=str,
|
||||||
|
optional=True,
|
||||||
|
default=None,
|
||||||
|
placeholder="Select the fruits",
|
||||||
|
description="The fruits you like.",
|
||||||
|
options=[
|
||||||
|
OptionValue(label="Apple", name="apple", value="apple"),
|
||||||
|
OptionValue(label="Banana", name="banana", value="banana"),
|
||||||
|
OptionValue(label="Orange", name="orange", value="orange"),
|
||||||
|
OptionValue(label="Pear", name="pear", value="pear"),
|
||||||
|
],
|
||||||
|
ui=ui.UIRadio(),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
inputs=[
|
||||||
|
IOField.build_from(
|
||||||
|
"User Name",
|
||||||
|
"user_name",
|
||||||
|
str,
|
||||||
|
description="The name of the user.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
outputs=[
|
||||||
|
IOField.build_from(
|
||||||
|
"Fruits",
|
||||||
|
"fruits",
|
||||||
|
str,
|
||||||
|
description="User's favorite fruits.",
|
||||||
|
)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self, fruits: Optional[str] = None, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.fruits = fruits
|
||||||
|
|
||||||
|
async def map(self, user_name: str) -> str:
|
||||||
|
"""Map the user name to the fruits."""
|
||||||
|
return "Your name is %s, and you like %s." % (user_name, self.fruits)
|
||||||
|
|
||||||
|
|
||||||
class ExampleFlowDatePickerOperator(MapOperator[str, str]):
|
class ExampleFlowDatePickerOperator(MapOperator[str, str]):
|
||||||
"""An example flow operator that includes a date picker as parameter."""
|
"""An example flow operator that includes a date picker as parameter."""
|
||||||
|
|
||||||
@@ -348,8 +401,13 @@ class ExampleFlowTextAreaOperator(MapOperator[str, str]):
|
|||||||
placeholder="Please input your comment",
|
placeholder="Please input your comment",
|
||||||
description="The comment you want to say.",
|
description="The comment you want to say.",
|
||||||
ui=ui.UITextArea(
|
ui=ui.UITextArea(
|
||||||
attr=ui.UITextArea.UIAttribute(show_count=True, maxlength=1000),
|
attr=ui.UITextArea.UIAttribute(
|
||||||
autosize=ui.UITextArea.AutoSize(min_rows=2, max_rows=6),
|
show_count=True,
|
||||||
|
maxlength=1000,
|
||||||
|
auto_size=ui.UITextArea.UIAttribute.AutoSize(
|
||||||
|
min_rows=2, max_rows=6
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
|
@@ -59,6 +59,7 @@ function GPTCard({
|
|||||||
return icon;
|
return icon;
|
||||||
}, [icon]);
|
}, [icon]);
|
||||||
|
|
||||||
|
// TODO: 算子资源标签
|
||||||
const tagNode = useMemo(() => {
|
const tagNode = useMemo(() => {
|
||||||
if (!tags || !tags.length) return null;
|
if (!tags || !tags.length) return null;
|
||||||
return (
|
return (
|
||||||
|
@@ -18,7 +18,7 @@ type CanvasNodeProps = {
|
|||||||
const ICON_PATH_PREFIX = '/icons/node/';
|
const ICON_PATH_PREFIX = '/icons/node/';
|
||||||
|
|
||||||
function TypeLabel({ label }: { label: string }) {
|
function TypeLabel({ label }: { label: string }) {
|
||||||
return <div className="w-full h-8 bg-stone-100 dark:bg-zinc-700 px-2 flex items-center justify-center">{label}</div>;
|
return <div className="w-full h-8 align-middle font-semibold">{label}</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CanvasNode: React.FC<CanvasNodeProps> = ({ data }) => {
|
const CanvasNode: React.FC<CanvasNodeProps> = ({ data }) => {
|
||||||
@@ -74,20 +74,22 @@ const CanvasNode: React.FC<CanvasNodeProps> = ({ data }) => {
|
|||||||
function renderOutput(data: IFlowNode) {
|
function renderOutput(data: IFlowNode) {
|
||||||
if (flowType === 'operator' && outputs?.length > 0) {
|
if (flowType === 'operator' && outputs?.length > 0) {
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="bg-zinc-100 dark:bg-zinc-700 rounded p-2">
|
||||||
<TypeLabel label="Outputs" />
|
<TypeLabel label="Outputs" />
|
||||||
{(outputs || []).map((output, index) => (
|
<div className="flex flex-col space-y-3">
|
||||||
<NodeHandler key={`${data.id}_input_${index}`} node={data} data={output} type="source" label="outputs" index={index} />
|
{outputs?.map((output, index) => (
|
||||||
))}
|
<NodeHandler key={`${data.id}_input_${index}`} node={data} data={output} type="source" label="outputs" index={index} />
|
||||||
</>
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
} else if (flowType === 'resource') {
|
} else if (flowType === 'resource') {
|
||||||
// resource nodes show output default
|
// resource nodes show output default
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="bg-zinc-100 dark:bg-zinc-700 rounded p-2">
|
||||||
<TypeLabel label="Outputs" />
|
<TypeLabel label="Outputs" />
|
||||||
<NodeHandler key={`${data.id}_input_0`} node={data} data={data} type="source" label="outputs" index={0} />
|
<NodeHandler key={`${data.id}_input_0`} node={data} data={data} type="source" label="outputs" index={0} />
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -105,7 +107,15 @@ const CanvasNode: React.FC<CanvasNodeProps> = ({ data }) => {
|
|||||||
<DeleteOutlined className="h-full text-lg cursor-pointer" onClick={deleteNode} />
|
<DeleteOutlined className="h-full text-lg cursor-pointer" onClick={deleteNode} />
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
<IconWrapper className="mt-2">
|
<IconWrapper className="mt-2">
|
||||||
<Tooltip title={<><p className="font-bold">{node.label}</p><p>{node.description}</p></>} placement="right">
|
<Tooltip
|
||||||
|
title={
|
||||||
|
<>
|
||||||
|
<p className="font-bold">{node.label}</p>
|
||||||
|
<p>{node.description}</p>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
placement="right"
|
||||||
|
>
|
||||||
<InfoCircleOutlined className="h-full text-lg cursor-pointer" />
|
<InfoCircleOutlined className="h-full text-lg cursor-pointer" />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</IconWrapper>
|
</IconWrapper>
|
||||||
@@ -113,36 +123,46 @@ const CanvasNode: React.FC<CanvasNodeProps> = ({ data }) => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={classNames('w-72 h-auto rounded-xl shadow-md p-0 border bg-white dark:bg-zinc-800 cursor-grab', {
|
className={classNames(
|
||||||
'border-blue-500': node.selected || isHovered,
|
'w-80 h-auto rounded-xl shadow-md px-2 py-4 border bg-white dark:bg-zinc-800 cursor-grab flex flex-col space-y-2 text-sm',
|
||||||
'border-stone-400 dark:border-white': !node.selected && !isHovered,
|
{
|
||||||
'border-dashed': flowType !== 'operator',
|
'border-blue-500': node.selected || isHovered,
|
||||||
'border-red-600': node.invalid,
|
'border-stone-400 dark:border-white': !node.selected && !isHovered,
|
||||||
})}
|
'border-dashed': flowType !== 'operator',
|
||||||
|
'border-red-600': node.invalid,
|
||||||
|
},
|
||||||
|
)}
|
||||||
onMouseEnter={onHover}
|
onMouseEnter={onHover}
|
||||||
onMouseLeave={onLeave}
|
onMouseLeave={onLeave}
|
||||||
>
|
>
|
||||||
{/* icon and label */}
|
{/* icon and label */}
|
||||||
<div className="flex flex-row items-center p-2">
|
<div className="flex flex-row items-center">
|
||||||
<Image src={'/icons/node/vis.png'} width={24} height={24} alt="" />
|
<Image src={'/icons/node/vis.png'} width={24} height={24} alt="" />
|
||||||
<p className="ml-2 text-lg font-bold text-ellipsis overflow-hidden whitespace-nowrap">{node.label}</p>
|
<p className="ml-2 text-lg font-bold text-ellipsis overflow-hidden whitespace-nowrap">{node.label}</p>
|
||||||
</div>
|
</div>
|
||||||
{inputs && inputs.length > 0 && (
|
|
||||||
<>
|
{inputs?.length > 0 && (
|
||||||
|
<div className="bg-zinc-100 dark:bg-zinc-700 rounded p-2">
|
||||||
<TypeLabel label="Inputs" />
|
<TypeLabel label="Inputs" />
|
||||||
{(inputs || []).map((input, index) => (
|
<div className="flex flex-col space-y-2">
|
||||||
<NodeHandler key={`${node.id}_input_${index}`} node={node} data={input} type="target" label="inputs" index={index} />
|
{inputs?.map((input, index) => (
|
||||||
))}
|
<NodeHandler key={`${node.id}_input_${index}`} node={node} data={input} type="target" label="inputs" index={index} />
|
||||||
</>
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{parameters && parameters.length > 0 && (
|
|
||||||
<>
|
{parameters?.length > 0 && (
|
||||||
|
<div className="bg-zinc-100 dark:bg-zinc-700 rounded p-2">
|
||||||
<TypeLabel label="Parameters" />
|
<TypeLabel label="Parameters" />
|
||||||
{(parameters || []).map((parameter, index) => (
|
<div className="flex flex-col space-y-3 text-neutral-500">
|
||||||
<NodeParamHandler key={`${node.id}_param_${index}`} node={node} data={parameter} label="parameters" index={index} />
|
{parameters?.map((parameter, index) => (
|
||||||
))}
|
<NodeParamHandler key={`${node.id}_param_${index}`} node={node} data={parameter} label="parameters" index={index} />
|
||||||
</>
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{renderOutput(node)}
|
{renderOutput(node)}
|
||||||
</div>
|
</div>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
@@ -94,15 +94,15 @@ const NodeHandler: React.FC<NodeHandlerProps> = ({ node, data, type, label, inde
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Handle
|
<Handle
|
||||||
className="w-2 h-2"
|
className={classNames('w-2 h-2', type === 'source' ? '-mr-4' : '-ml-4')}
|
||||||
type={type}
|
type={type}
|
||||||
position={type === 'source' ? Position.Right : Position.Left}
|
position={type === 'source' ? Position.Right : Position.Left}
|
||||||
id={`${node.id}|${label}|${index}`}
|
id={`${node.id}|${label}|${index}`}
|
||||||
isValidConnection={(connection) => isValidConnection(connection)}
|
isValidConnection={(connection) => isValidConnection(connection)}
|
||||||
/>
|
/>
|
||||||
<Typography
|
<Typography
|
||||||
className={classNames('p-2', {
|
className={classNames('bg-white dark:bg-[#232734] w-full px-2 py-1 rounded text-neutral-500',{
|
||||||
'pr-4': label === 'outputs',
|
'text-right': label === 'outputs',
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
|
@@ -17,6 +17,7 @@ import {
|
|||||||
RenderTextArea,
|
RenderTextArea,
|
||||||
RenderUpload,
|
RenderUpload,
|
||||||
RenderCodeEditor,
|
RenderCodeEditor,
|
||||||
|
RenderPassword,
|
||||||
} from './node-renderer';
|
} from './node-renderer';
|
||||||
|
|
||||||
interface NodeParamHandlerProps {
|
interface NodeParamHandlerProps {
|
||||||
@@ -40,7 +41,7 @@ const NodeParamHandler: React.FC<NodeParamHandlerProps> = ({ node, data, label,
|
|||||||
case 'int':
|
case 'int':
|
||||||
case 'float':
|
case 'float':
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<div className="text-sm">
|
||||||
<p>
|
<p>
|
||||||
{data.label}:<RequiredIcon optional={data.optional} />
|
{data.label}:<RequiredIcon optional={data.optional} />
|
||||||
{data.description && (
|
{data.description && (
|
||||||
@@ -60,7 +61,7 @@ const NodeParamHandler: React.FC<NodeParamHandlerProps> = ({ node, data, label,
|
|||||||
);
|
);
|
||||||
case 'str':
|
case 'str':
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<div className="text-sm">
|
||||||
<p>
|
<p>
|
||||||
{data.label}:<RequiredIcon optional={data.optional} />
|
{data.label}:<RequiredIcon optional={data.optional} />
|
||||||
{data.description && (
|
{data.description && (
|
||||||
@@ -87,11 +88,11 @@ const NodeParamHandler: React.FC<NodeParamHandlerProps> = ({ node, data, label,
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
case 'checkbox':
|
case 'bool':
|
||||||
defaultValue = defaultValue === 'False' ? false : defaultValue;
|
defaultValue = defaultValue === 'False' ? false : defaultValue;
|
||||||
defaultValue = defaultValue === 'True' ? true : defaultValue;
|
defaultValue = defaultValue === 'True' ? true : defaultValue;
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<div className="text-sm">
|
||||||
<p>
|
<p>
|
||||||
{data.label}:<RequiredIcon optional={data.optional} />
|
{data.label}:<RequiredIcon optional={data.optional} />
|
||||||
{data.description && (
|
{data.description && (
|
||||||
@@ -139,6 +140,8 @@ 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 'upload':
|
case 'upload':
|
||||||
return <RenderUpload {...props} />;
|
return <RenderUpload {...props} />;
|
||||||
|
@@ -13,15 +13,13 @@ export const RenderCascader = (params: Props) => {
|
|||||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<Cascader
|
||||||
<Cascader
|
{...attr}
|
||||||
{...attr}
|
options={data.options}
|
||||||
options={data.options}
|
defaultValue={defaultValue}
|
||||||
defaultValue={defaultValue}
|
placeholder="please select"
|
||||||
placeholder="please select"
|
className="w-full nodrag"
|
||||||
className="w-full nodrag"
|
onChange={onChange}
|
||||||
onChange={onChange}
|
/>
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -14,7 +14,7 @@ export const RenderCheckbox = (params: Props) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
data.options?.length > 0 && (
|
data.options?.length > 0 && (
|
||||||
<div className="p-2 text-sm">
|
<div className="bg-white p-2 rounded">
|
||||||
<Checkbox.Group {...attr} options={data.options} defaultValue={defaultValue} onChange={onChange} />
|
<Checkbox.Group {...attr} options={data.options} defaultValue={defaultValue} onChange={onChange} />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
@@ -11,6 +11,8 @@ 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 || {});
|
||||||
|
|
||||||
const onChangeDate: DatePickerProps['onChange'] = (date, dateString) => {
|
const onChangeDate: DatePickerProps['onChange'] = (date, dateString) => {
|
||||||
@@ -18,8 +20,12 @@ export const RenderDatePicker = (params: Props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<DatePicker
|
||||||
<DatePicker {...attr} className="w-full" defaultValue={dayjs(defaultValue)} onChange={onChangeDate} />
|
{...attr}
|
||||||
</div>
|
className="w-full"
|
||||||
|
placeholder="please select a date"
|
||||||
|
defaultValue={defaultValue ? dayjs(defaultValue) : null}
|
||||||
|
onChange={onChangeDate}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -10,3 +10,4 @@ export * from './time-picker';
|
|||||||
export * from './tree-select';
|
export * from './tree-select';
|
||||||
export * from './codeEditor';
|
export * from './codeEditor';
|
||||||
export * from './upload';
|
export * from './upload';
|
||||||
|
export * from './password';
|
||||||
|
@@ -13,16 +13,15 @@ export const RenderInput = (params: Props) => {
|
|||||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<Input
|
||||||
<Input
|
{...attr}
|
||||||
{...attr}
|
className="w-full"
|
||||||
className="w-full"
|
placeholder="please input"
|
||||||
placeholder="please input"
|
defaultValue={defaultValue}
|
||||||
defaultValue={defaultValue}
|
allowClear
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChange(e.target.value);
|
onChange(e.target.value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
18
web/components/flow/node-renderer/password.tsx
Normal file
18
web/components/flow/node-renderer/password.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { IFlowNodeParameter } from '@/types/flow';
|
||||||
|
import { Input } from 'antd';
|
||||||
|
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||||
|
|
||||||
|
const { Password } = Input;
|
||||||
|
|
||||||
|
type TextAreaProps = {
|
||||||
|
data: IFlowNodeParameter;
|
||||||
|
defaultValue: any;
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const RenderPassword = (params: TextAreaProps) => {
|
||||||
|
const { data, defaultValue, onChange } = params;
|
||||||
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
|
return <Password {...attr} placeholder="input password" defaultValue={defaultValue} onChange={onChange} />;
|
||||||
|
};
|
@@ -13,7 +13,7 @@ export const RenderRadio = (params: Props) => {
|
|||||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<div className="bg-white p-2 rounded">
|
||||||
<Radio.Group
|
<Radio.Group
|
||||||
{...attr}
|
{...attr}
|
||||||
options={data.options}
|
options={data.options}
|
||||||
|
@@ -13,15 +13,6 @@ export const RenderSelect = (params: Props) => {
|
|||||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<Select {...attr} className="w-full nodrag" placeholder="please select" defaultValue={defaultValue} options={data.options} onChange={onChange} />
|
||||||
<Select
|
|
||||||
{...attr}
|
|
||||||
className="w-full nodrag"
|
|
||||||
placeholder="please select"
|
|
||||||
defaultValue={defaultValue}
|
|
||||||
options={data.options}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -1,40 +0,0 @@
|
|||||||
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';
|
|
||||||
|
|
||||||
type TextAreaProps = {
|
|
||||||
data: IFlowNodeParameter;
|
|
||||||
defaultValue: any;
|
|
||||||
onChange: (value: any) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<div className="p-2 text-sm">
|
|
||||||
{data?.ui?.show_input ? (
|
|
||||||
<Row>
|
|
||||||
<Col span={12}>
|
|
||||||
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
|
||||||
</Col>
|
|
||||||
|
|
||||||
<Col span={4}>
|
|
||||||
<InputNumber {...attr} style={{ margin: '0 16px' }} value={inputValue} onChange={onChangeSlider} />
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
) : (
|
|
||||||
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@@ -21,7 +21,7 @@ export const RenderSlider = (params: TextAreaProps) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<>
|
||||||
{data?.ui?.show_input ? (
|
{data?.ui?.show_input ? (
|
||||||
<Row>
|
<Row>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
@@ -34,6 +34,6 @@ export const RenderSlider = (params: TextAreaProps) => {
|
|||||||
) : (
|
) : (
|
||||||
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
<Slider {...attr} onChange={onChangeSlider} value={typeof inputValue === 'number' ? inputValue : 0} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { IFlowNodeParameter } from '@/types/flow';
|
import { IFlowNodeParameter } from '@/types/flow';
|
||||||
import { Input } from 'antd';
|
import { Input } from 'antd';
|
||||||
import { convertKeysToCamelCase } from '@/utils/flow';
|
import { convertKeysToCamelCase } from '@/utils/flow';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
const { TextArea } = Input;
|
const { TextArea } = Input;
|
||||||
|
|
||||||
@@ -12,12 +13,12 @@ type TextAreaProps = {
|
|||||||
|
|
||||||
export const RenderTextArea = (params: TextAreaProps) => {
|
export const RenderTextArea = (params: TextAreaProps) => {
|
||||||
const { data, defaultValue, onChange } = params;
|
const { data, defaultValue, onChange } = params;
|
||||||
convertKeysToCamelCase(data?.ui?.attr?.autosize || {});
|
|
||||||
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
const attr = convertKeysToCamelCase(data.ui?.attr || {});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-2 text-sm">
|
<div className={classNames({ 'mb-3': attr.showCount === true })}>
|
||||||
<TextArea {...attr} defaultValue={defaultValue} onChange={(e) => onChange(e.target.value)} {...data.ui.attr.autosize} rows={4} />
|
<TextArea {...attr} defaultValue={defaultValue} onChange={onChange} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@@ -17,9 +17,5 @@ export const RenderTimePicker = (params: TextAreaProps) => {
|
|||||||
onChange(timeString);
|
onChange(timeString);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return <TimePicker {...attr} className="w-full" defaultValue={defaultValue} onChange={onChangeTime} placeholder="please select a moment" />;
|
||||||
<div className="p-2 text-sm">
|
|
||||||
<TimePicker {...attr} className="w-full" defaultValue={defaultValue} onChange={onChangeTime} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user