Merge branch 'feat/sprint-web-flow' into feat-sprint-web-flow-2.0

This commit is contained in:
云锋
2024-08-16 15:59:38 +08:00
committed by GitHub
20 changed files with 239 additions and 149 deletions

View File

@@ -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",
) )

View File

@@ -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 (

View File

@@ -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")

View File

@@ -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
),
),
), ),
) )
], ],

View File

@@ -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 (

View File

@@ -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>

View File

@@ -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

View File

@@ -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} />;

View File

@@ -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>
); );
}; };

View File

@@ -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>
) )

View File

@@ -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}
/>
); );
}; };

View File

@@ -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';

View File

@@ -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>
); );
}; };

View 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} />;
};

View File

@@ -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}

View File

@@ -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>
); );
}; };

View File

@@ -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>
);
};

View File

@@ -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> </>
); );
}; };

View File

@@ -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>
); );
}; };

View File

@@ -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>
);
}; };