feat(web): workflow variable type
This commit is contained in:
@@ -2342,6 +2342,11 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
|
||||
"eq": 'Is',
|
||||
"ne": 'Is Not',
|
||||
},
|
||||
file: {
|
||||
"empty": 'Not Exist',
|
||||
"not_empty": 'Exists',
|
||||
eq: 'All Are'
|
||||
},
|
||||
else_desc: 'Used to define the logic that should be executed when the if condition is not met.',
|
||||
unset: 'Condition Not Set',
|
||||
set: 'Set',
|
||||
|
||||
@@ -2303,6 +2303,11 @@ export const zh = {
|
||||
"eq": '是',
|
||||
"ne": '不是',
|
||||
},
|
||||
file: {
|
||||
"empty": '不存在',
|
||||
"not_empty": '存在',
|
||||
eq: '全都是'
|
||||
},
|
||||
else_desc: '用于定义当 if 条件不满足时应执行的逻辑。',
|
||||
unset: '条件未设置',
|
||||
set: '已设置',
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* @Last Modified time: 2026-04-08 11:05:34
|
||||
*/
|
||||
import { forwardRef, useImperativeHandle, useState, useRef, useMemo } from 'react';
|
||||
import { Form, Input, Select, InputNumber, Button, Row, Col, Flex, Spin } from 'antd';
|
||||
import { Form, Input, Select, InputNumber, Button, Row, Col, Flex } from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
|
||||
@@ -328,7 +328,7 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
|
||||
};
|
||||
|
||||
const content = (
|
||||
<Flex vertical gap={16} className="rb:max-h-[300px] rb:overflow-y-auto rb:p-3" style={{ minWidth: `${nodeWidth}px` }}>
|
||||
<Flex vertical gap={16} className="rb:max-h-75 rb:overflow-y-auto rb:p-3" style={{ minWidth: `${nodeWidth}px` }}>
|
||||
{nodeLibrary.map((category) => {
|
||||
const sourceNodeData = sourceNode?.getData();
|
||||
const isChildOfLoop = sourceNodeData?.cycle && graph?.getNodes().find((n: any) => n.getData()?.id === sourceNodeData.cycle && n.getData()?.type === 'loop');
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-03-25 15:23:45
|
||||
*/
|
||||
import { type FC } from 'react'
|
||||
import { useMemo, type FC } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Form, Button, Select, Space, Divider, InputNumber, type SelectProps, Flex, Row, Col } from 'antd'
|
||||
@@ -15,7 +15,7 @@ import Editor from '../../Editor'
|
||||
import { edgeAttrs, nodeWidth } from '../../../constant'
|
||||
import RbButton from '@/components/RbButton';
|
||||
import RadioGroupBtn from '../RadioGroupBtn'
|
||||
import { calcConditionNodeTotalHeight, getConditionNodeCasePortY } from '../../../utils'
|
||||
import { calcConditionNodeTotalHeight, getConditionNodeCasePortY } from '../../../utils';
|
||||
|
||||
interface CaseListProps {
|
||||
value?: Array<{ logical_operator: 'and' | 'or'; expressions: { left: string; operator: string; right: string; input_type?: string; }[] }>;
|
||||
@@ -49,6 +49,34 @@ const operatorsObj: { [key: string]: SelectProps['options'] } = {
|
||||
boolean: [
|
||||
{ value: 'eq', label: 'workflow.config.if-else.boolean.eq' },
|
||||
{ value: 'ne', label: 'workflow.config.if-else.boolean.ne' },
|
||||
],
|
||||
object: [
|
||||
{ value: 'eq', label: 'workflow.config.if-else.boolean.eq' },
|
||||
{ value: 'ne', label: 'workflow.config.if-else.boolean.ne' },
|
||||
{ value: 'empty', label: 'workflow.config.if-else.empty' },
|
||||
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
|
||||
],
|
||||
file: [
|
||||
{ value: 'empty', label: 'workflow.config.if-else.file.empty' },
|
||||
{ value: 'not_empty', label: 'workflow.config.if-else.file.not_empty' },
|
||||
],
|
||||
// TODO:包含、不包含、全都是
|
||||
'array[file]': [
|
||||
{ value: 'empty', label: 'workflow.config.if-else.empty' },
|
||||
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
|
||||
// { value: 'eq', label: 'workflow.config.if-else.eq' },
|
||||
// { value: 'contains', label: 'workflow.config.if-else.contains' },
|
||||
// { value: 'not_contains', label: 'workflow.config.if-else.not_contains' },
|
||||
],
|
||||
'array': [
|
||||
{ value: 'contains', label: 'workflow.config.if-else.contains' },
|
||||
{ value: 'not_contains', label: 'workflow.config.if-else.not_contains' },
|
||||
{ value: 'empty', label: 'workflow.config.if-else.empty' },
|
||||
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
|
||||
],
|
||||
'array[object]': [
|
||||
{ value: 'empty', label: 'workflow.config.if-else.empty' },
|
||||
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -247,6 +275,22 @@ const CaseList: FC<CaseListProps> = ({
|
||||
form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], undefined);
|
||||
};
|
||||
|
||||
const filterNumberOptions = useMemo(() => {
|
||||
const filterList: Suggestion[] = []
|
||||
options.forEach(vo => {
|
||||
if (vo.children && vo.children?.length > 0) {
|
||||
filterList.push({
|
||||
...vo,
|
||||
children: vo.children.filter(child => child.dataType === 'number')
|
||||
})
|
||||
} else if (vo.dataType === 'number') {
|
||||
filterList.push(vo)
|
||||
}
|
||||
})
|
||||
|
||||
return filterList
|
||||
}, [options])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form.List name={name}>
|
||||
@@ -284,11 +328,15 @@ const CaseList: FC<CaseListProps> = ({
|
||||
const currentCase = cases[caseIndex] || {};
|
||||
const currentExpression = currentCase.expressions?.[conditionIndex] || {};
|
||||
const currentOperator = currentExpression.operator;
|
||||
const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty';
|
||||
const leftFieldValue = currentExpression.left;
|
||||
const leftFieldOption = options.find(option => `{{${option.value}}}` === leftFieldValue);
|
||||
const leftFieldType = leftFieldOption?.dataType;
|
||||
const operatorList = operatorsObj[leftFieldType || 'default'] || operatorsObj.default || [];
|
||||
const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty' || leftFieldType === 'file' || leftFieldType === 'array[object]' || leftFieldType === 'array[file]';
|
||||
const operatorList = leftFieldType && operatorsObj[leftFieldType]
|
||||
? operatorsObj[leftFieldType]
|
||||
: leftFieldType && leftFieldType?.includes('array')
|
||||
? operatorsObj.array
|
||||
: operatorsObj.default;
|
||||
const inputType = leftFieldType === 'number' ? currentExpression.input_type : undefined;
|
||||
return (
|
||||
<Flex key={conditionField.key} gap={4} align="start" className="rb:mb-2!">
|
||||
@@ -312,7 +360,7 @@ const CaseList: FC<CaseListProps> = ({
|
||||
<Col flex="1">
|
||||
<Form.Item name={[conditionField.name, 'operator']} noStyle>
|
||||
<Select
|
||||
options={operatorList.map(vo => ({
|
||||
options={(operatorList ?? []).map(vo => ({
|
||||
...vo,
|
||||
label: t(String(vo?.label || ''))
|
||||
}))}
|
||||
@@ -328,7 +376,9 @@ const CaseList: FC<CaseListProps> = ({
|
||||
|
||||
{!hideRightField && (
|
||||
<div className="rb:py-1 rb:px-1.5">
|
||||
{leftFieldType === 'number'
|
||||
{leftFieldType === 'array[file]'
|
||||
? <>TODO</>
|
||||
: leftFieldType === 'number'
|
||||
? <Flex align="center">
|
||||
<Form.Item name={[conditionField.name, 'input_type']} noStyle>
|
||||
<Select
|
||||
@@ -345,24 +395,24 @@ const CaseList: FC<CaseListProps> = ({
|
||||
{inputType === 'variable'
|
||||
? <VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options.filter(vo => vo.dataType === 'number')}
|
||||
options={filterNumberOptions}
|
||||
allowClear={false}
|
||||
variant="borderless"
|
||||
size="small"
|
||||
/>
|
||||
: <InputNumber
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
variant="borderless"
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], value)}
|
||||
/>
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
variant="borderless"
|
||||
className="rb:w-full!"
|
||||
onChange={(value) => form.setFieldValue([name, caseIndex, 'expressions', conditionIndex, 'right'], value)}
|
||||
/>
|
||||
}
|
||||
</Form.Item>
|
||||
</Flex>
|
||||
: (
|
||||
<Form.Item name={[conditionField.name, 'right']} noStyle>
|
||||
{leftFieldType === 'boolean'
|
||||
? <RadioGroupBtn options={[ { value: true, label: 'True' }, { value: false, label: 'False' }]} type="inner" />
|
||||
{['boolean', 'array[boolean]'].includes(leftFieldType as string)
|
||||
? <RadioGroupBtn options={[{ value: true, label: 'True' }, { value: false, label: 'False' }]} type="inner" />
|
||||
: <Editor options={options} size="small" type="input" />
|
||||
}
|
||||
</Form.Item>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { type FC } from 'react'
|
||||
import { type FC, useMemo } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Form, Button, Select, InputNumber, Input, Divider, type SelectProps, Flex, Space, Row, Col } from 'antd'
|
||||
@@ -47,6 +47,18 @@ const operatorsObj: { [key: string]: SelectProps['options'] } = {
|
||||
{ value: 'ne', label: 'workflow.config.if-else.boolean.ne' },
|
||||
{ value: 'empty', label: 'workflow.config.if-else.empty' },
|
||||
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
|
||||
],
|
||||
// 为空、不为空
|
||||
object: [
|
||||
{ value: 'empty', label: 'workflow.config.if-else.empty' },
|
||||
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
|
||||
],
|
||||
// 包含、不包含、为空、不为空
|
||||
'array': [
|
||||
{ value: 'contains', label: 'workflow.config.if-else.contains' },
|
||||
{ value: 'not_contains', label: 'workflow.config.if-else.not_contains' },
|
||||
{ value: 'empty', label: 'workflow.config.if-else.empty' },
|
||||
{ value: 'not_empty', label: 'workflow.config.if-else.not_empty' },
|
||||
]
|
||||
}
|
||||
|
||||
@@ -81,6 +93,23 @@ const ConditionList: FC<CaseListProps> = ({
|
||||
const currentValue = form.getFieldValue([parentName, 'logical_operator']);
|
||||
form.setFieldValue([parentName, 'logical_operator'], currentValue === 'and' ? 'or' : 'and');
|
||||
};
|
||||
|
||||
const getNumVariable = useMemo(() => {
|
||||
const filterList: Suggestion[] = []
|
||||
options.forEach(variable => {
|
||||
if (variable.dataType === 'number') {
|
||||
filterList.push(variable)
|
||||
} else if (variable.dataType === 'file') {
|
||||
filterList.push({
|
||||
...variable,
|
||||
disabled: true,
|
||||
children: variable.children?.filter(child => child.dataType === 'number')
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return filterList
|
||||
}, [options])
|
||||
return (
|
||||
<>
|
||||
<Form.List name={[parentName, 'expressions']}>
|
||||
@@ -125,11 +154,19 @@ const ConditionList: FC<CaseListProps> = ({
|
||||
const expressions = form.getFieldValue([parentName, 'expressions']) || [];
|
||||
const currentExpression = expressions[index] || {};
|
||||
const currentOperator = currentExpression.operator;
|
||||
const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty';
|
||||
const leftFieldValue = currentExpression.left;
|
||||
const leftFieldOption = options.find(option => `{{${option.value}}}` === leftFieldValue);
|
||||
const leftFieldType = leftFieldOption?.dataType;
|
||||
const operatorList = operatorsObj[leftFieldType || 'default'] || operatorsObj.default || [];
|
||||
const hideRightField = currentOperator === 'empty' || currentOperator === 'not_empty' || ['array[object]', 'object'].includes(leftFieldType as string);
|
||||
const operatorList = leftFieldType && ['array[object]', 'object'].includes(leftFieldType)
|
||||
? operatorsObj.object
|
||||
: leftFieldType && ['array[boolean]', 'boolean'].includes(leftFieldType)
|
||||
? operatorsObj.boolean
|
||||
: leftFieldType && operatorsObj[leftFieldType]
|
||||
? operatorsObj[leftFieldType]
|
||||
: leftFieldType?.includes('array')
|
||||
? operatorsObj.array
|
||||
: operatorsObj.default
|
||||
const inputType = leftFieldType === 'number' ? currentExpression.input_type : undefined;
|
||||
return (
|
||||
<Flex
|
||||
@@ -146,10 +183,11 @@ const ConditionList: FC<CaseListProps> = ({
|
||||
<Form.Item name={[field.name, 'left']} noStyle>
|
||||
<VariableSelect
|
||||
options={options.filter(vo =>
|
||||
vo.value.includes('sys.') ||
|
||||
!['file', 'array[file]'].includes(vo.dataType) &&
|
||||
(vo.value.includes('sys.') ||
|
||||
vo.value.includes('conv.') ||
|
||||
vo.nodeData.type === 'loop' ||
|
||||
(vo.nodeData.cycle && vo.nodeData.cycle === selectedNode?.id)
|
||||
(vo.nodeData.cycle && vo.nodeData.cycle === selectedNode?.id))
|
||||
)}
|
||||
size="small"
|
||||
allowClear={false}
|
||||
@@ -163,7 +201,7 @@ const ConditionList: FC<CaseListProps> = ({
|
||||
<Col flex="96px">
|
||||
<Form.Item name={[field.name, 'operator']} noStyle>
|
||||
<Select
|
||||
options={operatorList.map(vo => ({
|
||||
options={(operatorList??[]).map(vo => ({
|
||||
...vo,
|
||||
label: t(String(vo?.label || ''))
|
||||
}))}
|
||||
@@ -198,7 +236,7 @@ const ConditionList: FC<CaseListProps> = ({
|
||||
? (
|
||||
<VariableSelect
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options.filter(vo => vo.dataType === 'number')}
|
||||
options={getNumVariable}
|
||||
allowClear={false}
|
||||
variant="borderless"
|
||||
size="small"
|
||||
@@ -219,7 +257,7 @@ const ConditionList: FC<CaseListProps> = ({
|
||||
: (
|
||||
<Form.Item name={[field.name, 'right']} noStyle>
|
||||
{leftFieldType === 'boolean'
|
||||
? <RadioGroupBtn options={[ { value: true, label: 'True' }, { value: false, label: 'False' }]} />
|
||||
? <RadioGroupBtn options={[ { value: true, label: 'True' }, { value: false, label: 'False' }]} type="inner" />
|
||||
: <Input variant="borderless" placeholder={t('common.pleaseEnter')} />
|
||||
}
|
||||
</Form.Item>
|
||||
|
||||
@@ -6,6 +6,7 @@ import VariableSelect from '../VariableSelect'
|
||||
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
|
||||
import RadioGroupBtn from '../RadioGroupBtn'
|
||||
import { getChildNodeVariables } from '../hooks/useVariableList'
|
||||
import CodeMirrorEditor from '@/components/CodeMirrorEditor';
|
||||
|
||||
interface CycleVar {
|
||||
name: string;
|
||||
@@ -28,11 +29,17 @@ const types = [
|
||||
'string',
|
||||
'number',
|
||||
'boolean',
|
||||
'object',
|
||||
'array[string]',
|
||||
'array[number]',
|
||||
'array[boolean]',
|
||||
'array[object]'
|
||||
]
|
||||
const object_placeholder = `# example
|
||||
# {
|
||||
# "name": "redbear",
|
||||
# "age": 2
|
||||
# }`
|
||||
|
||||
const CycleVarsList: FC<CycleVarsListProps> = ({
|
||||
value = [],
|
||||
@@ -144,6 +151,13 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
|
||||
{ value: true, label: 'True' },
|
||||
{ value: false, label: 'False' }]}
|
||||
/>
|
||||
: currentType === 'object'
|
||||
? <CodeMirrorEditor
|
||||
language="json"
|
||||
placeholder={object_placeholder}
|
||||
variant="outlined"
|
||||
size="small"
|
||||
/>
|
||||
: (
|
||||
<Input.TextArea
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
|
||||
@@ -45,6 +45,45 @@ const EditableTable: FC<EditableTableProps> = ({
|
||||
: options
|
||||
}, [options, filterBooleanType])
|
||||
|
||||
const namefilterOptions = useMemo(() => {
|
||||
const filterList: Suggestion[] = [];
|
||||
options.forEach(vo => {
|
||||
if (vo.dataType === 'file') {
|
||||
filterList.push({
|
||||
...vo,
|
||||
disabled: true,
|
||||
children: vo.children?.filter(child => child.dataType !== 'boolean')
|
||||
})
|
||||
} else if (vo.dataType !== 'array[file]') {
|
||||
filterList.push(vo)
|
||||
}
|
||||
})
|
||||
|
||||
return filterList
|
||||
}, [options])
|
||||
const valueFilterOptions = (type?: string) => {
|
||||
let filterOptions: Suggestion[] = []
|
||||
options.forEach(vo => {
|
||||
if (type === 'file' && vo.dataType === 'file') {
|
||||
filterOptions.push({
|
||||
...vo,
|
||||
children: []
|
||||
})
|
||||
} else if (type === 'file' && vo.dataType === 'array[file]') {
|
||||
filterOptions.push(vo)
|
||||
} else if (vo.dataType === 'file') {
|
||||
filterOptions.push({
|
||||
...vo,
|
||||
disabled: true
|
||||
})
|
||||
} else {
|
||||
filterOptions.push(vo)
|
||||
}
|
||||
})
|
||||
|
||||
return filterOptions
|
||||
}
|
||||
|
||||
const getColumns = (remove: (index: number) => void): TableProps<TableRow>['columns'] => {
|
||||
const hasType = typeOptions.length > 0;
|
||||
const contentClassName = hasType ? 'rb:w-[110px]!' : "rb:w-[148px]!"
|
||||
@@ -57,7 +96,7 @@ const EditableTable: FC<EditableTableProps> = ({
|
||||
render: (_: any, __: TableRow, index: number) => (
|
||||
<Form.Item name={[index, 'name']} className={formClassName}>
|
||||
<Editor
|
||||
options={booleanFilterOptions.filter(option => !option.dataType.includes('file'))}
|
||||
options={namefilterOptions}
|
||||
type="input"
|
||||
className={contentClassName}
|
||||
size={size}
|
||||
@@ -105,9 +144,7 @@ const EditableTable: FC<EditableTableProps> = ({
|
||||
>
|
||||
{(form) => {
|
||||
const currentType = form.getFieldValue([...Array.isArray(parentName) ? parentName : [parentName], index, 'type']);
|
||||
const filteredOptions = currentType === 'file'
|
||||
? booleanFilterOptions.filter(option => option.dataType.includes('file'))
|
||||
: booleanFilterOptions.filter(option => !option.dataType.includes('file'));
|
||||
const filteredOptions = valueFilterOptions(currentType)
|
||||
|
||||
return (
|
||||
<Form.Item name={[index, 'value']} className={formClassName}>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @Last Modified by: ZhaoYing
|
||||
* @Last Modified time: 2026-04-02 17:17:06
|
||||
*/
|
||||
import { type FC, useRef, useState } from "react";
|
||||
import { type FC, useMemo, useRef, useState } from "react";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Form, Row, Col, Select, Button, Divider, InputNumber, Switch, Input, Flex, Radio } from 'antd'
|
||||
import { CaretDownOutlined, CaretRightOutlined, SettingOutlined } from '@ant-design/icons';
|
||||
@@ -84,6 +84,64 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
setCollapsed((prev: boolean) => !prev)
|
||||
}
|
||||
|
||||
const filterVariables = useMemo(() => {
|
||||
const filterList: Suggestion[] = []
|
||||
options.forEach(variable => {
|
||||
if (['number', 'string'].includes(variable.dataType)) {
|
||||
filterList.push(variable)
|
||||
} else if (variable.dataType === 'file') {
|
||||
filterList.push({
|
||||
...variable,
|
||||
disabled: true,
|
||||
children: variable.children?.filter(child => ['number', 'string'].includes(child.dataType))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return filterList
|
||||
}, [options])
|
||||
const filterVariablesWithFile = useMemo(() => {
|
||||
const filterList: Suggestion[] = []
|
||||
options.forEach(variable => {
|
||||
if (['number', 'string', 'file', 'array[file]'].includes(variable.dataType)) {
|
||||
filterList.push(variable)
|
||||
}
|
||||
})
|
||||
|
||||
return filterList
|
||||
}, [options])
|
||||
const jsonRawFilterVariables = useMemo(() => {
|
||||
const filterList: Suggestion[] = []
|
||||
options.forEach(variable => {
|
||||
if (['number', 'string', 'array[string]', 'array[number]'].includes(variable.dataType)) {
|
||||
filterList.push(variable)
|
||||
} else if (variable.dataType === 'file') {
|
||||
filterList.push({
|
||||
...variable,
|
||||
disabled: true,
|
||||
children: variable.children?.filter(child => ['number', 'string', 'file', 'array[string]', 'array[number]'].includes(child.dataType))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return filterList
|
||||
}, [options])
|
||||
const fileFilterVariables = useMemo(() => {
|
||||
const filterList: Suggestion[] = []
|
||||
options.forEach(variable => {
|
||||
if (['array[file]'].includes(variable.dataType)) {
|
||||
filterList.push(variable)
|
||||
} else if (variable.dataType === 'file') {
|
||||
filterList.push({
|
||||
...variable,
|
||||
children: []
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return filterList
|
||||
}, [options])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Flex align="center" justify="space-between" className="rb:mb-1!">
|
||||
@@ -117,7 +175,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
<Form.Item name="url">
|
||||
<Editor
|
||||
key="url"
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
options={filterVariables}
|
||||
variant="outlined"
|
||||
type="input"
|
||||
size="small"
|
||||
@@ -134,7 +192,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
size="small"
|
||||
parentName="headers"
|
||||
title="HEADERS"
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
options={filterVariables}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -143,7 +201,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
size="small"
|
||||
parentName="params"
|
||||
title="PARAMS"
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
options={filterVariables}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
@@ -167,7 +225,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
<EditableTable
|
||||
size="small"
|
||||
parentName={['body', 'data']}
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number' || vo.dataType.includes('file'))}
|
||||
options={filterVariablesWithFile}
|
||||
typeOptions={[
|
||||
{ label: 'text', value: 'text' },
|
||||
{ label: 'file', value: 'file' }
|
||||
@@ -180,7 +238,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
<EditableTable
|
||||
size="small"
|
||||
parentName={['body', 'data']}
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
options={filterVariablesWithFile}
|
||||
filterBooleanType={true}
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -190,7 +248,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
<MessageEditor
|
||||
key="json"
|
||||
parentName={['body', 'data']}
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
options={jsonRawFilterVariables}
|
||||
isArray={false}
|
||||
title="JSON"
|
||||
titleVariant="borderless"
|
||||
@@ -204,7 +262,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
<MessageEditor
|
||||
key="raw"
|
||||
parentName={['body', 'data']}
|
||||
options={options.filter(vo => vo.dataType === 'string' || vo.dataType === 'number')}
|
||||
options={jsonRawFilterVariables}
|
||||
isArray={false}
|
||||
title="RAW TEXT"
|
||||
titleVariant="borderless"
|
||||
@@ -220,7 +278,7 @@ const HttpRequest: FC<{ options: Suggestion[]; selectedNode?: any; graphRef?: an
|
||||
<Editor
|
||||
key={['body', 'data'].join('_')}
|
||||
placeholder={t('common.pleaseSelect')}
|
||||
options={options.filter(vo => vo.dataType.includes('file'))}
|
||||
options={fileFilterVariables}
|
||||
type="input"
|
||||
size="small"
|
||||
height={28}
|
||||
|
||||
@@ -163,25 +163,45 @@ const ToolConfig: FC<{ options: Suggestion[]; }> = ({
|
||||
|
||||
form.setFieldsValue(inititalValue)
|
||||
}
|
||||
const getNumberOptions = useMemo(() => {
|
||||
const list: Suggestion[] = []
|
||||
|
||||
// string -> string
|
||||
// integer -> number
|
||||
// number -> number
|
||||
// boolean -> boolean【只能选true/false】
|
||||
// array -> array[file]/array[object]/array[string]/array[number]/array[boolean]
|
||||
// object -> object/file
|
||||
const getFilterOptions = (type: string) => {
|
||||
const filterList: Suggestion[] = [];
|
||||
options.forEach(vo => {
|
||||
if (vo.children && vo?.children?.length > 0) {
|
||||
const filterChild = vo.children.filter(child => child.dataType === 'number')
|
||||
if (vo.children && vo.children?.length > 0) {
|
||||
const childOptions = vo.children?.filter(child => child.dataType === type || (type === 'integer' && child.dataType === 'number'))
|
||||
|
||||
if (filterChild.length > 0) {
|
||||
list.push({ ...vo, disabled: vo.dataType !== 'number', children: filterChild })
|
||||
} else if (vo.dataType === 'number') {
|
||||
list.push({ ...vo, children: [] })
|
||||
if (vo.dataType === type
|
||||
|| (type === 'integer' && vo.dataType === 'number')
|
||||
|| (type === 'array' && vo.dataType.includes(type))
|
||||
|| (type === 'object' && vo.dataType === 'object')
|
||||
) {
|
||||
filterList.push({
|
||||
...vo,
|
||||
children: childOptions
|
||||
})
|
||||
} else if (childOptions.length > 0) {
|
||||
filterList.push({
|
||||
...vo,
|
||||
disabled: true,
|
||||
children: childOptions
|
||||
})
|
||||
}
|
||||
} else if (vo.dataType === 'number') {
|
||||
list.push({ ...vo })
|
||||
} else if (vo.dataType === type
|
||||
|| (type === 'integer' && vo.dataType === 'number')
|
||||
|| (type === 'array' && vo.dataType.includes(type))
|
||||
|| (type === 'object' && vo.dataType === 'object')) {
|
||||
filterList.push(vo)
|
||||
}
|
||||
})
|
||||
console.log('options', options, list)
|
||||
return list
|
||||
}, [options])
|
||||
|
||||
return filterList
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -205,7 +225,7 @@ const ToolConfig: FC<{ options: Suggestion[]; }> = ({
|
||||
<Form.Item
|
||||
name={['tool_parameters', parameter.name]}
|
||||
label={<>
|
||||
{parameter.name}
|
||||
{parameter.name} <span className="rb:text-[#5B6167] rb:mx-1">({parameter.type})</span>
|
||||
<Tooltip title={parameter.description} placement="right">
|
||||
<div className="rb:size-3 rb:ml-0.5 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/question.svg')]"></div>
|
||||
</Tooltip>
|
||||
@@ -220,21 +240,12 @@ const ToolConfig: FC<{ options: Suggestion[]; }> = ({
|
||||
? <Select size="small" options={parameter.enum.map(vo => ({ value: vo, label: vo }))} placeholder={t('common.pleaseSelect')} />
|
||||
: parameter.type === 'boolean'
|
||||
? <Switch size="small" />
|
||||
: parameter.type === 'integer' || parameter.type === 'number'
|
||||
? <Editor
|
||||
variant="outlined"
|
||||
type="input"
|
||||
size="small"
|
||||
height={28}
|
||||
options={getNumberOptions}
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
/>
|
||||
: <Editor
|
||||
variant="outlined"
|
||||
type="input"
|
||||
size="small"
|
||||
height={28}
|
||||
options={options}
|
||||
options={getFilterOptions(parameter.type)}
|
||||
placeholder={t('common.pleaseEnter')}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ const Properties: FC<PropertiesProps> = ({
|
||||
return null;
|
||||
})() : null;
|
||||
|
||||
let filteredList = variableList.filter(variable => variable.dataType !== 'boolean');
|
||||
let filteredList = variableList.filter(variable => !['boolean', 'object', 'array[boolean]'].includes(variable.dataType));
|
||||
|
||||
// If this LLM node is a child of iteration/loop, ensure parent variables are included
|
||||
if (parentLoopNode) {
|
||||
@@ -315,7 +315,46 @@ const Properties: FC<PropertiesProps> = ({
|
||||
|
||||
return filteredList;
|
||||
}
|
||||
if (nodeType === 'knowledge-retrieval' || nodeType === 'parameter-extractor' && key !== 'prompt' || nodeType === 'memory-read' || nodeType === 'question-classifier') {
|
||||
if (nodeType === 'knowledge-retrieval'
|
||||
|| (nodeType === 'parameter-extractor' && key === 'text')
|
||||
|| (nodeType === 'question-classifier' && ['input_variable', 'categories'].includes(key as string))
|
||||
) {
|
||||
const allList = addParentIterationVars(variableList);
|
||||
let filteredList: Suggestion[] = []
|
||||
allList.forEach(variable => {
|
||||
if (variable.dataType === 'string') {
|
||||
filteredList.push(variable)
|
||||
} else if (variable.dataType === 'file') {
|
||||
filteredList.push({
|
||||
...variable,
|
||||
children: variable.children.filter((child: Suggestion) => child.dataType === 'string')
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return filteredList
|
||||
}
|
||||
|
||||
if ((nodeType === 'parameter-extractor' && key === 'prompt')
|
||||
|| (nodeType === 'question-classifier' && key === 'user_supplement_prompt')
|
||||
) {
|
||||
const allList = addParentIterationVars(variableList);
|
||||
let filteredList: Suggestion[] = []
|
||||
allList.forEach(variable => {
|
||||
if (['string', 'number'].includes(variable.dataType)) {
|
||||
filteredList.push(variable)
|
||||
} else if (variable.dataType === 'file') {
|
||||
filteredList.push({
|
||||
...variable,
|
||||
disabled: true,
|
||||
children: variable.children.filter((child: Suggestion) => ['string', 'number'].includes(child.dataType))
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return filteredList
|
||||
}
|
||||
if (nodeType === 'memory-read') {
|
||||
let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string');
|
||||
return filteredList;
|
||||
}
|
||||
@@ -327,11 +366,24 @@ const Properties: FC<PropertiesProps> = ({
|
||||
let filteredList = addParentIterationVars(variableList).filter(variable => variable.dataType === 'string' || variable.dataType === 'number');
|
||||
return filteredList;
|
||||
}
|
||||
if (nodeType === 'iteration' && key === 'output' || nodeType === 'loop' && key === 'condition') {
|
||||
|
||||
if ((nodeType === 'iteration' && key === 'output')) {
|
||||
if (!selectedNode) return [];
|
||||
let filteredList = nodeType === 'iteration'
|
||||
? variableList.filter(variable => variable.value.includes('sys.'))
|
||||
: addParentIterationVars(variableList).filter(variable => variable.nodeData.type !== 'loop');
|
||||
let filteredList = variableList.filter(variable => variable.value.includes('sys.'))
|
||||
const childVariables = getChildNodeVariables(selectedNode, graphRef);
|
||||
const existingKeys = new Set(filteredList.map(v => v.key));
|
||||
childVariables.forEach(v => {
|
||||
if (!existingKeys.has(v.key)) {
|
||||
filteredList.push(v);
|
||||
existingKeys.add(v.key);
|
||||
}
|
||||
});
|
||||
|
||||
return filteredList.filter(variable => variable.dataType !== 'array[file]');
|
||||
}
|
||||
if (nodeType === 'loop' && key === 'condition') {
|
||||
if (!selectedNode) return [];
|
||||
let filteredList = addParentIterationVars(variableList).filter(variable => variable.nodeData.type !== 'loop');
|
||||
|
||||
const childVariables = getChildNodeVariables(selectedNode, graphRef);
|
||||
const existingKeys = new Set(filteredList.map(v => v.key));
|
||||
@@ -348,6 +400,25 @@ const Properties: FC<PropertiesProps> = ({
|
||||
return variableList.filter(variable => variable.dataType.includes('array'));
|
||||
}
|
||||
|
||||
if (nodeType === 'code'
|
||||
|| (nodeType === 'if-else' && key === 'cases')
|
||||
) {
|
||||
const allList = addParentIterationVars(variableList);
|
||||
let filteredList: Suggestion[] = []
|
||||
allList.forEach(variable => {
|
||||
if (variable.dataType === 'file') {
|
||||
filteredList.push({
|
||||
...variable,
|
||||
disabled: true,
|
||||
})
|
||||
} else {
|
||||
filteredList.push(variable)
|
||||
}
|
||||
})
|
||||
|
||||
return filteredList
|
||||
}
|
||||
|
||||
// For all other node types, add parent iteration variables if applicable
|
||||
let baseList = variableList;
|
||||
return addParentIterationVars(baseList);
|
||||
|
||||
Reference in New Issue
Block a user