mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-21 11:43:56 +00:00
Feat: IfElse node support variable reference (#5025)
* if else node support reference (#5016) * if else node support reference * optimize input render * ui * fix --------- Co-authored-by: Archer <545436317@qq.com> * fix: chat * perf: download invoice * optimize ifelse node ui (#5024) * perf: ifelse node * optimize type (#5027) --------- Co-authored-by: heheer <heheer@sealos.io>
This commit is contained in:
@@ -12,7 +12,8 @@ weight: 788
|
|||||||
1. AI proxy 监控完善,支持以图表/表格形式查看模型调用和性能情况。
|
1. AI proxy 监控完善,支持以图表/表格形式查看模型调用和性能情况。
|
||||||
2. HTTP 节点和 MCP 支持单独“鉴权配置”,鉴权配置明文不会二次返回客户端,以保障数据安全。
|
2. HTTP 节点和 MCP 支持单独“鉴权配置”,鉴权配置明文不会二次返回客户端,以保障数据安全。
|
||||||
3. 问题分类和内容提取,提示词中自动加入上一轮结果进行额外引导。
|
3. 问题分类和内容提取,提示词中自动加入上一轮结果进行额外引导。
|
||||||
4. 商业版支持知识库分块时,LLM 进行自动分段识别。
|
4. 判断器支持变量引用。
|
||||||
|
5. 商业版支持知识库分块时,LLM 进行自动分段识别。
|
||||||
|
|
||||||
## ⚙️ 优化
|
## ⚙️ 优化
|
||||||
|
|
||||||
|
@@ -113,3 +113,15 @@ export const allConditionList = [
|
|||||||
value: VariableConditionEnum.lengthLessThanOrEqualTo
|
value: VariableConditionEnum.lengthLessThanOrEqualTo
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
export const renderNumberConditionList = new Set<VariableConditionEnum>([
|
||||||
|
VariableConditionEnum.greaterThan,
|
||||||
|
VariableConditionEnum.greaterThanOrEqualTo,
|
||||||
|
VariableConditionEnum.lessThan,
|
||||||
|
VariableConditionEnum.lessThanOrEqualTo,
|
||||||
|
VariableConditionEnum.lengthEqualTo,
|
||||||
|
VariableConditionEnum.lengthNotEqualTo,
|
||||||
|
VariableConditionEnum.lengthGreaterThan,
|
||||||
|
VariableConditionEnum.lengthGreaterThanOrEqualTo,
|
||||||
|
VariableConditionEnum.lengthLessThan,
|
||||||
|
VariableConditionEnum.lengthLessThanOrEqualTo
|
||||||
|
]);
|
||||||
|
@@ -37,7 +37,8 @@ export const IfElseNode: FlowNodeTemplateType = {
|
|||||||
{
|
{
|
||||||
variable: undefined,
|
variable: undefined,
|
||||||
condition: undefined,
|
condition: undefined,
|
||||||
value: undefined
|
value: undefined,
|
||||||
|
valueType: 'input'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -5,7 +5,8 @@ export type IfElseConditionType = 'AND' | 'OR';
|
|||||||
export type ConditionListItemType = {
|
export type ConditionListItemType = {
|
||||||
variable?: ReferenceItemValueType;
|
variable?: ReferenceItemValueType;
|
||||||
condition?: VariableConditionEnum;
|
condition?: VariableConditionEnum;
|
||||||
value?: string;
|
value?: string | ReferenceItemValueType;
|
||||||
|
valueType?: 'input' | 'reference';
|
||||||
};
|
};
|
||||||
export type IfElseListItemType = {
|
export type IfElseListItemType = {
|
||||||
condition: IfElseConditionType;
|
condition: IfElseConditionType;
|
||||||
|
@@ -1,7 +1,10 @@
|
|||||||
import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import type { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||||
import { type DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
import {
|
||||||
|
type RuntimeNodeItemType,
|
||||||
|
type DispatchNodeResultType
|
||||||
|
} from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import {
|
import {
|
||||||
IfElseResultEnum,
|
IfElseResultEnum,
|
||||||
VariableConditionEnum
|
VariableConditionEnum
|
||||||
@@ -14,6 +17,7 @@ import {
|
|||||||
import { type ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
import { type ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
|
||||||
import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils';
|
import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
import { getReferenceVariableValue } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||||
|
import { type ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||||
|
|
||||||
type Props = ModuleDispatchProps<{
|
type Props = ModuleDispatchProps<{
|
||||||
[NodeInputKeyEnum.condition]: IfElseConditionType;
|
[NodeInputKeyEnum.condition]: IfElseConditionType;
|
||||||
@@ -49,7 +53,7 @@ function isInclude(value: any, target: any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkCondition(condition: VariableConditionEnum, inputValue: any, value: string) {
|
function checkCondition(condition: VariableConditionEnum, inputValue: any, value: any) {
|
||||||
const operations: Record<VariableConditionEnum, () => boolean> = {
|
const operations: Record<VariableConditionEnum, () => boolean> = {
|
||||||
[VariableConditionEnum.isEmpty]: () => isEmpty(inputValue),
|
[VariableConditionEnum.isEmpty]: () => isEmpty(inputValue),
|
||||||
[VariableConditionEnum.isNotEmpty]: () => !isEmpty(inputValue),
|
[VariableConditionEnum.isNotEmpty]: () => !isEmpty(inputValue),
|
||||||
@@ -100,19 +104,29 @@ function checkCondition(condition: VariableConditionEnum, inputValue: any, value
|
|||||||
function getResult(
|
function getResult(
|
||||||
condition: IfElseConditionType,
|
condition: IfElseConditionType,
|
||||||
list: ConditionListItemType[],
|
list: ConditionListItemType[],
|
||||||
variables: any,
|
variables: Record<string, any>,
|
||||||
runtimeNodes: any[]
|
runtimeNodes: RuntimeNodeItemType[]
|
||||||
) {
|
) {
|
||||||
const listResult = list.map((item) => {
|
const listResult = list.map((item) => {
|
||||||
const { variable, condition: variableCondition, value } = item;
|
const { variable, condition: variableCondition, value, valueType } = item;
|
||||||
|
if (!variableCondition) return;
|
||||||
|
|
||||||
const inputValue = getReferenceVariableValue({
|
const conditionLeftValue = getReferenceVariableValue({
|
||||||
value: variable,
|
value: variable,
|
||||||
variables,
|
variables,
|
||||||
nodes: runtimeNodes
|
nodes: runtimeNodes
|
||||||
});
|
});
|
||||||
|
|
||||||
return checkCondition(variableCondition as VariableConditionEnum, inputValue, value || '');
|
const conditionRightValue =
|
||||||
|
valueType === 'reference'
|
||||||
|
? getReferenceVariableValue({
|
||||||
|
value: value as ReferenceItemValueType,
|
||||||
|
variables,
|
||||||
|
nodes: runtimeNodes
|
||||||
|
})
|
||||||
|
: value;
|
||||||
|
|
||||||
|
return checkCondition(variableCondition, conditionLeftValue, conditionRightValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
return condition === 'AND' ? listResult.every(Boolean) : listResult.some(Boolean);
|
return condition === 'AND' ? listResult.every(Boolean) : listResult.some(Boolean);
|
||||||
|
@@ -8,6 +8,7 @@ export const iconPaths = {
|
|||||||
chatSend: () => import('./icons/chatSend.svg'),
|
chatSend: () => import('./icons/chatSend.svg'),
|
||||||
check: () => import('./icons/check.svg'),
|
check: () => import('./icons/check.svg'),
|
||||||
checkCircle: () => import('./icons/checkCircle.svg'),
|
checkCircle: () => import('./icons/checkCircle.svg'),
|
||||||
|
circleMinus: () => import('./icons/circleMinus.svg'),
|
||||||
close: () => import('./icons/close.svg'),
|
close: () => import('./icons/close.svg'),
|
||||||
closeSolid: () => import('./icons/closeSolid.svg'),
|
closeSolid: () => import('./icons/closeSolid.svg'),
|
||||||
code: () => import('./icons/code.svg'),
|
code: () => import('./icons/code.svg'),
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" >
|
||||||
|
<path d="M5.33342 7.80417C4.99499 7.80417 4.72063 8.07852 4.72063 8.41695C4.72063 8.75538 4.99499 9.02974 5.33342 9.02974H10.6667C11.0052 9.02974 11.2795 8.75538 11.2795 8.41695C11.2795 8.07852 11.0052 7.80417 10.6667 7.80417H5.33342Z" />
|
||||||
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.7901 8.41695C14.7901 12.167 11.7501 15.207 8.00008 15.207C4.25007 15.207 1.21008 12.167 1.21008 8.41695C1.21008 4.66694 4.25007 1.62695 8.00008 1.62695C11.7501 1.62695 14.7901 4.66694 14.7901 8.41695ZM13.4567 8.41695C13.4567 11.4306 11.0137 13.8736 8.00008 13.8736C4.98645 13.8736 2.54342 11.4306 2.54342 8.41695C2.54342 5.40332 4.98645 2.96029 8.00008 2.96029C11.0137 2.96029 13.4567 5.40332 13.4567 8.41695Z"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 778 B |
@@ -1,4 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 17" >
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18">
|
||||||
<path d="M5.33342 7.80417C4.99499 7.80417 4.72063 8.07852 4.72063 8.41695C4.72063 8.75538 4.99499 9.02974 5.33342 9.02974H10.6667C11.0052 9.02974 11.2795 8.75538 11.2795 8.41695C11.2795 8.07852 11.0052 7.80417 10.6667 7.80417H5.33342Z" />
|
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.15002 9C3.15002 8.58579 3.48581 8.25 3.90002 8.25H14.1C14.5142 8.25 14.85 8.58579 14.85 9C14.85 9.41421 14.5142 9.75 14.1 9.75H3.90002C3.48581 9.75 3.15002 9.41421 3.15002 9Z" />
|
||||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.7901 8.41695C14.7901 12.167 11.7501 15.207 8.00008 15.207C4.25007 15.207 1.21008 12.167 1.21008 8.41695C1.21008 4.66694 4.25007 1.62695 8.00008 1.62695C11.7501 1.62695 14.7901 4.66694 14.7901 8.41695ZM13.4567 8.41695C13.4567 11.4306 11.0137 13.8736 8.00008 13.8736C4.98645 13.8736 2.54342 11.4306 2.54342 8.41695C2.54342 5.40332 4.98645 2.96029 8.00008 2.96029C11.0137 2.96029 13.4567 5.40332 13.4567 8.41695Z"/>
|
|
||||||
</svg>
|
</svg>
|
Before Width: | Height: | Size: 778 B After Width: | Height: | Size: 300 B |
@@ -4,7 +4,8 @@ import {
|
|||||||
NumberInputField,
|
NumberInputField,
|
||||||
NumberInputStepper,
|
NumberInputStepper,
|
||||||
NumberDecrementStepper,
|
NumberDecrementStepper,
|
||||||
type NumberInputProps
|
type NumberInputProps,
|
||||||
|
type NumberInputFieldProps
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import MyIcon from '../../Icon';
|
import MyIcon from '../../Icon';
|
||||||
@@ -16,11 +17,11 @@ type Props = Omit<NumberInputProps, 'onChange' | 'onBlur'> & {
|
|||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
register?: UseFormRegister<any>;
|
register?: UseFormRegister<any>;
|
||||||
name?: string;
|
name?: string;
|
||||||
bg?: string;
|
inputFieldProps?: NumberInputFieldProps;
|
||||||
};
|
};
|
||||||
|
|
||||||
const MyNumberInput = (props: Props) => {
|
const MyNumberInput = (props: Props) => {
|
||||||
const { register, name, onChange, onBlur, placeholder, bg, ...restProps } = props;
|
const { register, name, onChange, onBlur, placeholder, inputFieldProps, ...restProps } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NumberInput
|
<NumberInput
|
||||||
@@ -68,7 +69,6 @@ const MyNumberInput = (props: Props) => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<NumberInputField
|
<NumberInputField
|
||||||
bg={bg}
|
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
h={restProps.h}
|
h={restProps.h}
|
||||||
defaultValue={restProps.defaultValue}
|
defaultValue={restProps.defaultValue}
|
||||||
@@ -80,6 +80,7 @@ const MyNumberInput = (props: Props) => {
|
|||||||
valueAsNumber: true
|
valueAsNumber: true
|
||||||
})
|
})
|
||||||
: {})}
|
: {})}
|
||||||
|
{...inputFieldProps}
|
||||||
/>
|
/>
|
||||||
<NumberInputStepper>
|
<NumberInputStepper>
|
||||||
<NumberIncrementStepper>
|
<NumberIncrementStepper>
|
||||||
|
@@ -26,7 +26,7 @@ export const MultipleRowSelect = ({
|
|||||||
onSelect,
|
onSelect,
|
||||||
ButtonProps,
|
ButtonProps,
|
||||||
changeOnEverySelect = false,
|
changeOnEverySelect = false,
|
||||||
rowMinWidth = 'autp'
|
rowMinWidth = 'auto'
|
||||||
}: MultipleSelectProps & {
|
}: MultipleSelectProps & {
|
||||||
rowMinWidth?: string;
|
rowMinWidth?: string;
|
||||||
}) => {
|
}) => {
|
||||||
@@ -66,7 +66,7 @@ export const MultipleRowSelect = ({
|
|||||||
if (currentScrollTop !== undefined && MenuRef.current[index]) {
|
if (currentScrollTop !== undefined && MenuRef.current[index]) {
|
||||||
MenuRef.current[index]!.scrollTop = currentScrollTop;
|
MenuRef.current[index]!.scrollTop = currentScrollTop;
|
||||||
}
|
}
|
||||||
}, [cloneValue, currentScrollTop]);
|
}, [currentScrollTop, index]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -143,7 +143,7 @@ export const MultipleRowSelect = ({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[cloneValue]
|
[changeOnEverySelect, cloneValue, emptyTip, maxH, minWidth, onClose, onSelect, rowMinWidth, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onOpenSelect = useCallback(() => {
|
const onOpenSelect = useCallback(() => {
|
||||||
@@ -172,7 +172,6 @@ export const MultipleRowSelect = ({
|
|||||||
ref={ButtonRef}
|
ref={ButtonRef}
|
||||||
width={'100%'}
|
width={'100%'}
|
||||||
px={3}
|
px={3}
|
||||||
rightIcon={<MyIcon name={'core/chat/chevronDown'} w={4} color={'myGray.500'} />}
|
|
||||||
variant={'whitePrimaryOutline'}
|
variant={'whitePrimaryOutline'}
|
||||||
size={'lg'}
|
size={'lg'}
|
||||||
fontSize={'sm'}
|
fontSize={'sm'}
|
||||||
@@ -189,7 +188,12 @@ export const MultipleRowSelect = ({
|
|||||||
: {})}
|
: {})}
|
||||||
{...ButtonProps}
|
{...ButtonProps}
|
||||||
>
|
>
|
||||||
<Box>{label ?? placeholder}</Box>
|
<Flex alignItems={'center'}>
|
||||||
|
<Box flex="1" overflow="hidden" textOverflow="ellipsis" whiteSpace="nowrap">
|
||||||
|
{label ?? placeholder}
|
||||||
|
</Box>
|
||||||
|
<MyIcon name={'core/chat/chevronDown'} w={4} flexShrink={0} color={'myGray.500'} />
|
||||||
|
</Flex>
|
||||||
</MenuButton>
|
</MenuButton>
|
||||||
<MenuList
|
<MenuList
|
||||||
className={ButtonProps?.className}
|
className={ButtonProps?.className}
|
||||||
@@ -260,7 +264,7 @@ export const MultipleRowArraySelect = ({
|
|||||||
});
|
});
|
||||||
onSelect(validList);
|
onSelect(validList);
|
||||||
},
|
},
|
||||||
[onSelect]
|
[list, onSelect]
|
||||||
);
|
);
|
||||||
|
|
||||||
const RenderList = useCallback(
|
const RenderList = useCallback(
|
||||||
@@ -334,13 +338,13 @@ export const MultipleRowArraySelect = ({
|
|||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[navigationPath, formatValue, onSelect]
|
[navigationPath, maxH, emptyTip, t, formatValue, onChange]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onOpenSelect = useCallback(() => {
|
const onOpenSelect = useCallback(() => {
|
||||||
setNavigationPath([]);
|
setNavigationPath([]);
|
||||||
onOpen();
|
onOpen();
|
||||||
}, []);
|
}, [onOpen]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box ref={ref} position={'relative'}>
|
<Box ref={ref} position={'relative'}>
|
||||||
|
@@ -25,31 +25,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.contentEditable_isFlow {
|
|
||||||
position: relative;
|
|
||||||
height: 100%;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid var(--chakra-colors-myGray-200);
|
|
||||||
border-radius: var(--chakra-radii-sm);
|
|
||||||
padding: 6px 8px;
|
|
||||||
font-size: var(--chakra-fontSizes-sm);
|
|
||||||
overflow-y: auto;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: var(--chakra-colors-primary-300);
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
color: var(--chakra-colors-myGray-100);
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background-color: var(--chakra-colors-myGray-200) !important;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
&::-webkit-scrollbar-thumb:hover {
|
|
||||||
background-color: var(--chakra-colors-myGray-250) !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.contentEditable:focus {
|
.contentEditable:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
border: 1px solid;
|
border: 1px solid;
|
||||||
|
@@ -21,6 +21,8 @@
|
|||||||
"can_not_loop": "This node can't loop.",
|
"can_not_loop": "This node can't loop.",
|
||||||
"choose_another_application_to_call": "Select another application to call",
|
"choose_another_application_to_call": "Select another application to call",
|
||||||
"classification_result": "Classification Result",
|
"classification_result": "Classification Result",
|
||||||
|
"click_to_change_reference": "Click to switch input mode",
|
||||||
|
"click_to_change_value": "Click to switch reference mode",
|
||||||
"code.Reset template": "Reset Template",
|
"code.Reset template": "Reset Template",
|
||||||
"code.Reset template confirm": "Confirm reset code template? This will reset all inputs and outputs to template values. Please save your current code.",
|
"code.Reset template confirm": "Confirm reset code template? This will reset all inputs and outputs to template values. Please save your current code.",
|
||||||
"code.Switch language confirm": "Switching the language will reset the code, will it continue?",
|
"code.Switch language confirm": "Switching the language will reset the code, will it continue?",
|
||||||
|
@@ -21,6 +21,8 @@
|
|||||||
"can_not_loop": "该节点不支持循环嵌套",
|
"can_not_loop": "该节点不支持循环嵌套",
|
||||||
"choose_another_application_to_call": "选择一个其他应用进行调用",
|
"choose_another_application_to_call": "选择一个其他应用进行调用",
|
||||||
"classification_result": "分类结果",
|
"classification_result": "分类结果",
|
||||||
|
"click_to_change_reference": "点击切换输入模式",
|
||||||
|
"click_to_change_value": "点击切换变量引用模式",
|
||||||
"code.Reset template": "还原模板",
|
"code.Reset template": "还原模板",
|
||||||
"code.Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。",
|
"code.Reset template confirm": "确认还原代码模板?将会重置所有输入和输出至模板值,请注意保存当前代码。",
|
||||||
"code.Switch language confirm": "切换语言将重置代码,是否继续?",
|
"code.Switch language confirm": "切换语言将重置代码,是否继续?",
|
||||||
|
@@ -21,6 +21,8 @@
|
|||||||
"can_not_loop": "這個節點不能迴圈。",
|
"can_not_loop": "這個節點不能迴圈。",
|
||||||
"choose_another_application_to_call": "選擇另一個應用程式來呼叫",
|
"choose_another_application_to_call": "選擇另一個應用程式來呼叫",
|
||||||
"classification_result": "分類結果",
|
"classification_result": "分類結果",
|
||||||
|
"click_to_change_reference": "點擊切換輸入模式",
|
||||||
|
"click_to_change_value": "點擊切換變量引用模式",
|
||||||
"code.Reset template": "重設範本",
|
"code.Reset template": "重設範本",
|
||||||
"code.Reset template confirm": "確定要重設程式碼範本嗎?這將會把所有輸入和輸出重設為範本值。請儲存您目前的程式碼。",
|
"code.Reset template confirm": "確定要重設程式碼範本嗎?這將會把所有輸入和輸出重設為範本值。請儲存您目前的程式碼。",
|
||||||
"code.Switch language confirm": "切換語言將重設代碼,是否繼續?",
|
"code.Switch language confirm": "切換語言將重設代碼,是否繼續?",
|
||||||
|
@@ -94,7 +94,7 @@ export const VariableInputItem = ({
|
|||||||
step={1}
|
step={1}
|
||||||
min={item.min}
|
min={item.min}
|
||||||
max={item.max}
|
max={item.max}
|
||||||
bg={'white'}
|
inputFieldProps={{ bg: 'white' }}
|
||||||
value={value}
|
value={value}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
isInvalid={errors?.variables && Object.keys(errors.variables).includes(item.key)}
|
isInvalid={errors?.variables && Object.keys(errors.variables).includes(item.key)}
|
||||||
@@ -250,18 +250,16 @@ const VariableInput = ({
|
|||||||
<ExternalVariableInputItem key={item.id} item={item} variablesForm={variablesForm} />
|
<ExternalVariableInputItem key={item.id} item={item} variablesForm={variablesForm} />
|
||||||
))}
|
))}
|
||||||
{variableList.length === 0 && !chatStarted && (
|
{variableList.length === 0 && !chatStarted && (
|
||||||
<Box>
|
<Button
|
||||||
<Button
|
leftIcon={<MyIcon name={'core/chat/chatFill'} w={'16px'} />}
|
||||||
leftIcon={<MyIcon name={'core/chat/chatFill'} w={'16px'} />}
|
size={'sm'}
|
||||||
size={'sm'}
|
maxW={'100px'}
|
||||||
maxW={'100px'}
|
onClick={handleSubmitChat(() => {
|
||||||
onClick={handleSubmitChat(() => {
|
chatForm.setValue('chatStarted', true);
|
||||||
chatForm.setValue('chatStarted', true);
|
})}
|
||||||
})}
|
>
|
||||||
>
|
{t('common:core.chat.Start Chat')}
|
||||||
{t('common:core.chat.Start Chat')}
|
</Button>
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
)}
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -287,7 +285,6 @@ const VariableInput = ({
|
|||||||
size={'sm'}
|
size={'sm'}
|
||||||
maxW={'100px'}
|
maxW={'100px'}
|
||||||
onClick={handleSubmitChat(() => {
|
onClick={handleSubmitChat(() => {
|
||||||
console.log('start chat');
|
|
||||||
chatForm.setValue('chatStarted', true);
|
chatForm.setValue('chatStarted', true);
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
|
@@ -198,7 +198,6 @@ const RenderPluginInput = ({
|
|||||||
step={1}
|
step={1}
|
||||||
min={input.min}
|
min={input.min}
|
||||||
max={input.max}
|
max={input.max}
|
||||||
bg={'myGray.50'}
|
|
||||||
isDisabled={isDisabled}
|
isDisabled={isDisabled}
|
||||||
isInvalid={isInvalid}
|
isInvalid={isInvalid}
|
||||||
value={value}
|
value={value}
|
||||||
|
@@ -146,10 +146,10 @@ export const FormInputComponent = React.memo(function FormInputComponent({
|
|||||||
max={max}
|
max={max}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
isDisabled={submitted}
|
isDisabled={submitted}
|
||||||
bg={'white'}
|
|
||||||
register={register}
|
register={register}
|
||||||
name={label}
|
name={label}
|
||||||
isRequired={required}
|
isRequired={required}
|
||||||
|
inputFieldProps={{ bg: 'white' }}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case FlowNodeInputTypeEnum.select:
|
case FlowNodeInputTypeEnum.select:
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { getInvoiceRecords, readInvoiceFile } from '@/web/support/wallet/bill/invoice/api';
|
import { getInvoiceRecords } from '@/web/support/wallet/bill/invoice/api';
|
||||||
import MyBox from '@fastgpt/web/components/common/MyBox';
|
import MyBox from '@fastgpt/web/components/common/MyBox';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
@@ -23,6 +23,7 @@ import dayjs from 'dayjs';
|
|||||||
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
|
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
import { downloadFetch } from '@/web/common/system/utils';
|
||||||
|
|
||||||
const InvoiceTable = () => {
|
const InvoiceTable = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -138,25 +139,10 @@ function InvoiceDetailModal({
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { runAsync: handleDownloadInvoice } = useRequest2(async (id: string) => {
|
const { runAsync: handleDownloadInvoice } = useRequest2(async (id: string) => {
|
||||||
const fileInfo = await readInvoiceFile(id);
|
await downloadFetch({
|
||||||
|
url: `/api/proApi/support/wallet/bill/invoice/downloadFile?id=${id}`,
|
||||||
// Blob
|
filename: `${invoice.teamName}.pdf`
|
||||||
const byteCharacters = atob(fileInfo.data);
|
});
|
||||||
const byteNumbers = new Array(byteCharacters.length);
|
|
||||||
for (let i = 0; i < byteCharacters.length; i++) {
|
|
||||||
byteNumbers[i] = byteCharacters.charCodeAt(i);
|
|
||||||
}
|
|
||||||
const byteArray = new Uint8Array(byteNumbers);
|
|
||||||
const blob = new Blob([byteArray], { type: fileInfo.mimeType });
|
|
||||||
const fileUrl = URL.createObjectURL(blob);
|
|
||||||
|
|
||||||
// preview
|
|
||||||
window.open(fileUrl, '_blank');
|
|
||||||
|
|
||||||
// clean
|
|
||||||
setTimeout(() => {
|
|
||||||
URL.revokeObjectURL(fileUrl);
|
|
||||||
}, 1000);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@@ -214,15 +214,7 @@ const RenderToolInput = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (paramInfo.type === 'number') {
|
if (paramInfo.type === 'number') {
|
||||||
return (
|
return <MyNumberInput step={1} isInvalid={isInvalid} value={value} onChange={onChange} />;
|
||||||
<MyNumberInput
|
|
||||||
step={1}
|
|
||||||
bg={'myGray.50'}
|
|
||||||
isInvalid={isInvalid}
|
|
||||||
value={value}
|
|
||||||
onChange={onChange}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (paramInfo.type === 'boolean') {
|
if (paramInfo.type === 'boolean') {
|
||||||
return <Switch isChecked={value} onChange={onChange} isInvalid={isInvalid} />;
|
return <Switch isChecked={value} onChange={onChange} isInvalid={isInvalid} />;
|
||||||
|
@@ -39,7 +39,7 @@ const NodeCQNode = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
<MyIcon
|
<MyIcon
|
||||||
mt={1}
|
mt={1}
|
||||||
mr={2}
|
mr={2}
|
||||||
name={'minus'}
|
name={'circleMinus'}
|
||||||
w={'12px'}
|
w={'12px'}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
color={'myGray.600'}
|
color={'myGray.600'}
|
||||||
|
@@ -85,7 +85,7 @@ const NodeDatasetConcat = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
step={100}
|
step={100}
|
||||||
value={item.value}
|
value={item.value}
|
||||||
name={NodeInputKeyEnum.datasetMaxTokens}
|
name={NodeInputKeyEnum.datasetMaxTokens}
|
||||||
bg={'white'}
|
inputFieldProps={{ bg: 'white' }}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
nodeId,
|
nodeId,
|
||||||
|
@@ -1,16 +1,16 @@
|
|||||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
import { Box, Button, Flex, HStack } from '@chakra-ui/react';
|
||||||
import {
|
import {
|
||||||
type DraggableProvided,
|
type DraggableProvided,
|
||||||
type DraggableStateSnapshot
|
type DraggableStateSnapshot
|
||||||
} from '@fastgpt/web/components/common/DndDrag/index';
|
} from '@fastgpt/web/components/common/DndDrag/index';
|
||||||
import Container from '../../components/Container';
|
import Container from '../../components/Container';
|
||||||
import { MinusIcon, SmallAddIcon } from '@chakra-ui/icons';
|
import { MinusIcon } from '@chakra-ui/icons';
|
||||||
import { type IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
import { type IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { type ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io';
|
import { type ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { ReferSelector, useReference } from '../render/RenderInput/templates/Reference';
|
import { ReferSelector, useReference } from '../render/RenderInput/templates/Reference';
|
||||||
import { WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
import { VARIABLE_NODE_ID, WorkflowIOValueTypeEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import {
|
import {
|
||||||
VariableConditionEnum,
|
VariableConditionEnum,
|
||||||
allConditionList,
|
allConditionList,
|
||||||
@@ -18,6 +18,7 @@ import {
|
|||||||
booleanConditionList,
|
booleanConditionList,
|
||||||
numberConditionList,
|
numberConditionList,
|
||||||
objectConditionList,
|
objectConditionList,
|
||||||
|
renderNumberConditionList,
|
||||||
stringConditionList
|
stringConditionList
|
||||||
} from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
} from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
@@ -28,9 +29,12 @@ import MyInput from '@/components/MyInput';
|
|||||||
import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils';
|
import { getElseIFLabel, getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { MySourceHandle } from '../render/Handle';
|
import { MySourceHandle } from '../render/Handle';
|
||||||
import { Position, useReactFlow } from 'reactflow';
|
import { Position, useReactFlow } from 'reactflow';
|
||||||
import { getRefData } from '@/web/core/workflow/utils';
|
import { getRefData, getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
||||||
import DragIcon from '@fastgpt/web/components/common/DndDrag/DragIcon';
|
import DragIcon from '@fastgpt/web/components/common/DndDrag/DragIcon';
|
||||||
import { AppContext } from '@/pageComponents/app/detail/context';
|
import { AppContext } from '@/pageComponents/app/detail/context';
|
||||||
|
import MyNumberInput from '@fastgpt/web/components/common/Input/NumberInput';
|
||||||
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
|
import MyIconButton from '@fastgpt/web/components/common/Icon/button';
|
||||||
|
|
||||||
const ListItem = ({
|
const ListItem = ({
|
||||||
provided,
|
provided,
|
||||||
@@ -57,7 +61,6 @@ const ListItem = ({
|
|||||||
const Render = useMemo(() => {
|
const Render = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<Flex
|
<Flex
|
||||||
alignItems={'center'}
|
|
||||||
position={'relative'}
|
position={'relative'}
|
||||||
transform={snapshot.isDragging ? `scale(${getZoom()})` : ''}
|
transform={snapshot.isDragging ? `scale(${getZoom()})` : ''}
|
||||||
transformOrigin={'top left'}
|
transformOrigin={'top left'}
|
||||||
@@ -66,16 +69,20 @@ const ListItem = ({
|
|||||||
<Container w={snapshot.isDragging ? '' : 'full'} className="nodrag">
|
<Container w={snapshot.isDragging ? '' : 'full'} className="nodrag">
|
||||||
<Flex mb={4} alignItems={'center'}>
|
<Flex mb={4} alignItems={'center'}>
|
||||||
{ifElseList.length > 1 && <DragIcon provided={provided} />}
|
{ifElseList.length > 1 && <DragIcon provided={provided} />}
|
||||||
<Box color={'black'} fontSize={'md'} ml={2}>
|
<Box color={'myGray.900'} fontWeight={'medium'} fontSize={'md'} ml={2}>
|
||||||
{getElseIFLabel(conditionIndex)}
|
{getElseIFLabel(conditionIndex)}
|
||||||
</Box>
|
</Box>
|
||||||
{conditionItem.list?.length > 1 && (
|
{conditionItem.list?.length > 1 && (
|
||||||
<Flex
|
<Flex
|
||||||
px={'2.5'}
|
ml={1.5}
|
||||||
|
px={1}
|
||||||
color={'primary.600'}
|
color={'primary.600'}
|
||||||
fontWeight={'medium'}
|
fontWeight={'medium'}
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
|
_hover={{
|
||||||
|
bg: 'myGray.200'
|
||||||
|
}}
|
||||||
rounded={'md'}
|
rounded={'md'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onUpdateIfElseList(
|
onUpdateIfElseList(
|
||||||
@@ -95,7 +102,7 @@ const ListItem = ({
|
|||||||
<MyIcon ml={1} boxSize={5} name="change" />
|
<MyIcon ml={1} boxSize={5} name="change" />
|
||||||
</Flex>
|
</Flex>
|
||||||
)}
|
)}
|
||||||
<Box flex={1}></Box>
|
<Box flex={1} />
|
||||||
{ifElseList.length > 1 && (
|
{ifElseList.length > 1 && (
|
||||||
<MyIcon
|
<MyIcon
|
||||||
ml={2}
|
ml={2}
|
||||||
@@ -103,7 +110,7 @@ const ListItem = ({
|
|||||||
name="delete"
|
name="delete"
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
_hover={{ color: 'red.600' }}
|
_hover={{ color: 'red.600' }}
|
||||||
color={'myGray.400'}
|
color={'myGray.600'}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onUpdateIfElseList(ifElseList.filter((_, index) => index !== conditionIndex));
|
onUpdateIfElseList(ifElseList.filter((_, index) => index !== conditionIndex));
|
||||||
onDelEdge({
|
onDelEdge({
|
||||||
@@ -119,102 +126,96 @@ const ListItem = ({
|
|||||||
return (
|
return (
|
||||||
<Box key={i}>
|
<Box key={i}>
|
||||||
{/* condition list */}
|
{/* condition list */}
|
||||||
<Flex gap={2} mb={2} alignItems={'center'}>
|
<Flex gap={1.5} mb={2} alignItems={'center'}>
|
||||||
{/* variable reference */}
|
{/* variable reference */}
|
||||||
<Box minW={'250px'}>
|
<VariableSelector
|
||||||
<VariableSelector
|
nodeId={nodeId}
|
||||||
nodeId={nodeId}
|
variable={item.variable}
|
||||||
variable={item.variable}
|
onSelect={(e) => {
|
||||||
onSelect={(e) => {
|
onUpdateIfElseList(
|
||||||
onUpdateIfElseList(
|
ifElseList.map((ifElse, index) => {
|
||||||
ifElseList.map((ifElse, index) => {
|
if (index === conditionIndex) {
|
||||||
if (index === conditionIndex) {
|
|
||||||
return {
|
|
||||||
...ifElse,
|
|
||||||
list: ifElse.list.map((item, index) => {
|
|
||||||
if (index === i) {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
variable: e,
|
|
||||||
condition: undefined
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return ifElse;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
{/* condition select */}
|
|
||||||
<Box w={'130px'} flex={1}>
|
|
||||||
<ConditionSelect
|
|
||||||
condition={item.condition}
|
|
||||||
variable={item.variable}
|
|
||||||
onSelect={(e) => {
|
|
||||||
onUpdateIfElseList(
|
|
||||||
ifElseList.map((ifElse, index) => {
|
|
||||||
if (index === conditionIndex) {
|
|
||||||
return {
|
|
||||||
...ifElse,
|
|
||||||
list: ifElse.list.map((item, index) => {
|
|
||||||
if (index === i) {
|
|
||||||
return {
|
|
||||||
...item,
|
|
||||||
condition: e
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return item;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return ifElse;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
{/* value */}
|
|
||||||
<Box w={'200px'}>
|
|
||||||
<ConditionValueInput
|
|
||||||
value={item.value}
|
|
||||||
condition={item.condition}
|
|
||||||
variable={item.variable}
|
|
||||||
onChange={(e) => {
|
|
||||||
onUpdateIfElseList(
|
|
||||||
ifElseList.map((ifElse, index) => {
|
|
||||||
return {
|
return {
|
||||||
...ifElse,
|
...ifElse,
|
||||||
list:
|
list: ifElse.list.map((item, index) => {
|
||||||
index === conditionIndex
|
if (index === i) {
|
||||||
? ifElse.list.map((item, index) => {
|
return {
|
||||||
if (index === i) {
|
...item,
|
||||||
return {
|
variable: e,
|
||||||
...item,
|
condition: undefined
|
||||||
value: e
|
};
|
||||||
};
|
}
|
||||||
}
|
return item;
|
||||||
return item;
|
})
|
||||||
})
|
|
||||||
: ifElse.list
|
|
||||||
};
|
};
|
||||||
})
|
}
|
||||||
);
|
return ifElse;
|
||||||
}}
|
})
|
||||||
/>
|
);
|
||||||
</Box>
|
}}
|
||||||
|
/>
|
||||||
|
{/* condition select */}
|
||||||
|
<ConditionSelect
|
||||||
|
condition={item.condition}
|
||||||
|
variable={item.variable}
|
||||||
|
onSelect={(e) => {
|
||||||
|
onUpdateIfElseList(
|
||||||
|
ifElseList.map((ifElse, index) => {
|
||||||
|
if (index === conditionIndex) {
|
||||||
|
return {
|
||||||
|
...ifElse,
|
||||||
|
list: ifElse.list.map((item, index) => {
|
||||||
|
if (index === i) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
condition: e
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return ifElse;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{/* value */}
|
||||||
|
<ConditionValueInput
|
||||||
|
value={item.value}
|
||||||
|
valueType={item.valueType}
|
||||||
|
condition={item.condition}
|
||||||
|
variable={item.variable}
|
||||||
|
nodeId={nodeId}
|
||||||
|
updateValue={(value, valueType) => {
|
||||||
|
onUpdateIfElseList(
|
||||||
|
ifElseList.map((ifElse, index) => {
|
||||||
|
return {
|
||||||
|
...ifElse,
|
||||||
|
list:
|
||||||
|
index === conditionIndex
|
||||||
|
? ifElse.list.map((item, index) => {
|
||||||
|
if (index === i) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
value,
|
||||||
|
valueType
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
})
|
||||||
|
: ifElse.list
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
{/* delete */}
|
{/* delete */}
|
||||||
{conditionItem.list.length > 1 && (
|
{conditionItem.list.length > 1 && (
|
||||||
<MinusIcon
|
<MyIconButton
|
||||||
ml={2}
|
icon="minus"
|
||||||
boxSize={3}
|
hoverColor={'red.600'}
|
||||||
name="delete"
|
hoverBg="red.100"
|
||||||
cursor={'pointer'}
|
|
||||||
_hover={{ color: 'red.600' }}
|
|
||||||
color={'myGray.400'}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onUpdateIfElseList(
|
onUpdateIfElseList(
|
||||||
ifElseList.map((ifElse, index) => {
|
ifElseList.map((ifElse, index) => {
|
||||||
@@ -235,30 +236,32 @@ const ListItem = ({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Box>
|
</Box>
|
||||||
<Button
|
<Flex>
|
||||||
onClick={() => {
|
<Button
|
||||||
onUpdateIfElseList(
|
onClick={() => {
|
||||||
ifElseList.map((ifElse, index) => {
|
onUpdateIfElseList(
|
||||||
if (index === conditionIndex) {
|
ifElseList.map((ifElse, index) => {
|
||||||
return {
|
if (index === conditionIndex) {
|
||||||
...ifElse,
|
return {
|
||||||
list: ifElse.list.concat({
|
...ifElse,
|
||||||
variable: undefined,
|
list: ifElse.list.concat({
|
||||||
condition: undefined,
|
variable: undefined,
|
||||||
value: undefined
|
condition: undefined,
|
||||||
})
|
value: undefined
|
||||||
};
|
})
|
||||||
}
|
};
|
||||||
return ifElse;
|
}
|
||||||
})
|
return ifElse;
|
||||||
);
|
})
|
||||||
}}
|
);
|
||||||
variant={'link'}
|
}}
|
||||||
leftIcon={<SmallAddIcon />}
|
variant={'link'}
|
||||||
color={'primary.600'}
|
leftIcon={<MyIcon name={'common/addLight'} boxSize={4} mr={-1} />}
|
||||||
>
|
color={'primary.700'}
|
||||||
{t('common:core.module.input.add')}
|
>
|
||||||
</Button>
|
{t('common:core.module.input.add')}
|
||||||
|
</Button>
|
||||||
|
</Flex>
|
||||||
</Container>
|
</Container>
|
||||||
{!snapshot.isDragging && (
|
{!snapshot.isDragging && (
|
||||||
<MySourceHandle
|
<MySourceHandle
|
||||||
@@ -324,6 +327,11 @@ const VariableSelector = ({
|
|||||||
value={variable}
|
value={variable}
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
isArray={false}
|
isArray={false}
|
||||||
|
ButtonProps={{
|
||||||
|
w: '14rem',
|
||||||
|
borderColor: 'myGray.200',
|
||||||
|
borderRadius: 'sm'
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -392,7 +400,9 @@ const ConditionSelect = ({
|
|||||||
return (
|
return (
|
||||||
<MySelect
|
<MySelect
|
||||||
className="nowheel"
|
className="nowheel"
|
||||||
w={'100%'}
|
w={'135px'}
|
||||||
|
h={10}
|
||||||
|
borderColor={'myGray.200'}
|
||||||
list={filterQuiredConditionList}
|
list={filterQuiredConditionList}
|
||||||
value={condition}
|
value={condition}
|
||||||
onChange={onSelect}
|
onChange={onSelect}
|
||||||
@@ -401,74 +411,192 @@ const ConditionSelect = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
|
||||||
Different condition can be entered differently
|
|
||||||
empty, notEmpty: forbid input
|
|
||||||
boolean type: select true/false
|
|
||||||
*/
|
|
||||||
const ConditionValueInput = ({
|
const ConditionValueInput = ({
|
||||||
value = '',
|
value,
|
||||||
|
valueType: type,
|
||||||
variable,
|
variable,
|
||||||
condition,
|
condition,
|
||||||
onChange
|
updateValue,
|
||||||
|
nodeId
|
||||||
}: {
|
}: {
|
||||||
value?: string;
|
value?: string | ReferenceItemValueType;
|
||||||
|
valueType?: 'input' | 'reference';
|
||||||
variable?: ReferenceItemValueType;
|
variable?: ReferenceItemValueType;
|
||||||
condition?: VariableConditionEnum;
|
condition?: VariableConditionEnum;
|
||||||
onChange: (e: string) => void;
|
updateValue: (value: string | ReferenceItemValueType, valueType: 'input' | 'reference') => void;
|
||||||
|
nodeId: string;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
|
||||||
|
const appDetail = useContextSelector(AppContext, (v) => v.appDetail);
|
||||||
|
|
||||||
|
const isReference = useMemo(() => type === 'reference', [type]);
|
||||||
|
|
||||||
|
const globalVariables = getWorkflowGlobalVariables({
|
||||||
|
nodes: nodeList,
|
||||||
|
chatConfig: appDetail.chatConfig
|
||||||
|
});
|
||||||
|
|
||||||
// get value type
|
// get value type
|
||||||
const valueType = useMemo(() => {
|
const valueType = useMemo(() => {
|
||||||
if (!variable) return;
|
if (variable?.[0] === VARIABLE_NODE_ID) {
|
||||||
const node = nodeList.find((node) => node.nodeId === variable[0]);
|
return globalVariables.find((item) => item.key === variable[1])?.valueType;
|
||||||
|
} else {
|
||||||
|
const node = nodeList.find((node) => node.nodeId === variable?.[0]);
|
||||||
|
const output = node?.outputs.find((item) => item.id === variable?.[1]);
|
||||||
|
return output?.valueType;
|
||||||
|
}
|
||||||
|
}, [globalVariables, nodeList, variable]);
|
||||||
|
const { referenceList } = useReference({
|
||||||
|
nodeId,
|
||||||
|
valueType
|
||||||
|
});
|
||||||
|
|
||||||
if (!node) return WorkflowIOValueTypeEnum.any;
|
const showBooleanSelect = useMemo(() => {
|
||||||
const output = node.outputs.find((item) => item.id === variable[1]);
|
return (
|
||||||
|
valueType === WorkflowIOValueTypeEnum.boolean ||
|
||||||
|
(valueType === WorkflowIOValueTypeEnum.arrayBoolean &&
|
||||||
|
condition &&
|
||||||
|
!renderNumberConditionList.has(condition))
|
||||||
|
);
|
||||||
|
}, [condition, valueType]);
|
||||||
|
const showNumberInput = useMemo(() => {
|
||||||
|
return (
|
||||||
|
valueType === WorkflowIOValueTypeEnum.number ||
|
||||||
|
valueType === WorkflowIOValueTypeEnum.arrayNumber ||
|
||||||
|
(valueType?.includes('array') && condition && renderNumberConditionList.has(condition))
|
||||||
|
);
|
||||||
|
}, [condition, valueType]);
|
||||||
|
|
||||||
if (!output) return WorkflowIOValueTypeEnum.any;
|
const RenderInput = useMemo(() => {
|
||||||
return output.valueType;
|
if (showBooleanSelect) {
|
||||||
}, [nodeList, variable]);
|
|
||||||
|
|
||||||
const Render = useMemo(() => {
|
|
||||||
if (valueType === WorkflowIOValueTypeEnum.boolean) {
|
|
||||||
return (
|
return (
|
||||||
<MySelect
|
<MySelect
|
||||||
list={[
|
list={[
|
||||||
{ label: 'True', value: 'true' },
|
{ label: 'True', value: 'true' },
|
||||||
{ label: 'False', value: 'false' }
|
{ label: 'False', value: 'false' }
|
||||||
]}
|
]}
|
||||||
onChange={onChange}
|
onChange={(e) => updateValue(e, 'input')}
|
||||||
value={value}
|
value={value as string}
|
||||||
placeholder={t('workflow:ifelse.Select value')}
|
placeholder={t('workflow:ifelse.Select value')}
|
||||||
isDisabled={
|
borderLeftRadius={0}
|
||||||
condition === VariableConditionEnum.isEmpty ||
|
h={10}
|
||||||
condition === VariableConditionEnum.isNotEmpty
|
borderColor={'myGray.200'}
|
||||||
}
|
/>
|
||||||
|
);
|
||||||
|
} else if (showNumberInput) {
|
||||||
|
return (
|
||||||
|
<MyNumberInput
|
||||||
|
step={1}
|
||||||
|
inputFieldProps={{
|
||||||
|
bg: 'white',
|
||||||
|
borderLeftRadius: 'none'
|
||||||
|
}}
|
||||||
|
value={Number(value as string)}
|
||||||
|
onChange={(e) => updateValue(String(e), 'input')}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<MyInput
|
<MyInput
|
||||||
value={value}
|
value={value as string}
|
||||||
placeholder={
|
placeholder={
|
||||||
condition === VariableConditionEnum.reg
|
condition === VariableConditionEnum.reg
|
||||||
? '/^((+|00)86)?1[3-9]d{9}$/'
|
? '/^((+|00)86)?1[3-9]d{9}$/'
|
||||||
: t('workflow:ifelse.Input value')
|
: t('workflow:ifelse.Input value')
|
||||||
}
|
}
|
||||||
w={'100%'}
|
w={'full'}
|
||||||
|
h={'full'}
|
||||||
bg={'white'}
|
bg={'white'}
|
||||||
isDisabled={
|
borderLeftRadius={0}
|
||||||
condition === VariableConditionEnum.isEmpty ||
|
onChange={(e) => updateValue(e.target.value, 'input')}
|
||||||
condition === VariableConditionEnum.isNotEmpty
|
|
||||||
}
|
|
||||||
onChange={(e) => onChange(e.target.value)}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [condition, onChange, value, valueType, t]);
|
}, [showBooleanSelect, showNumberInput, value, t, condition, updateValue]);
|
||||||
|
|
||||||
return Render;
|
const RenderReference = useMemo(() => {
|
||||||
|
return (
|
||||||
|
<ReferSelector
|
||||||
|
placeholder={t('common:select_reference_variable')}
|
||||||
|
list={referenceList}
|
||||||
|
value={isReference ? (value as ReferenceItemValueType) : undefined}
|
||||||
|
onSelect={(e) => {
|
||||||
|
updateValue(e as ReferenceItemValueType, 'reference');
|
||||||
|
}}
|
||||||
|
isArray={false}
|
||||||
|
ButtonProps={{
|
||||||
|
borderRadius: 'sm',
|
||||||
|
borderLeftRadius: 'none',
|
||||||
|
borderColor: 'myGray.200',
|
||||||
|
w: '100%'
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}, [t, referenceList, isReference, value, updateValue]);
|
||||||
|
|
||||||
|
const isDisabled =
|
||||||
|
condition === VariableConditionEnum.isEmpty || condition === VariableConditionEnum.isNotEmpty;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex position="relative">
|
||||||
|
<Flex>
|
||||||
|
<MyTooltip
|
||||||
|
label={
|
||||||
|
isReference
|
||||||
|
? t('workflow:click_to_change_reference')
|
||||||
|
: t('workflow:click_to_change_value')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<HStack
|
||||||
|
w={'4rem'}
|
||||||
|
h={10}
|
||||||
|
border={'1px solid'}
|
||||||
|
borderRight={'none'}
|
||||||
|
borderColor={'myGray.200'}
|
||||||
|
borderLeftRadius={'sm'}
|
||||||
|
justifyContent={'center'}
|
||||||
|
bg={'white'}
|
||||||
|
px={2}
|
||||||
|
spacing={2}
|
||||||
|
cursor={'pointer'}
|
||||||
|
_hover={{
|
||||||
|
bg: 'myGray.50'
|
||||||
|
}}
|
||||||
|
onClick={() => {
|
||||||
|
if (isDisabled) return;
|
||||||
|
|
||||||
|
if (isReference) {
|
||||||
|
updateValue('', 'input');
|
||||||
|
} else {
|
||||||
|
updateValue(['', undefined], 'reference');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isReference ? (
|
||||||
|
<MyIcon name={'core/workflow/inputType/reference'} w={4} color={'primary.600'} />
|
||||||
|
) : (
|
||||||
|
<MyIcon name={'core/app/variable/input'} w={4} color={'primary.600'} />
|
||||||
|
)}
|
||||||
|
<MyIcon name={'common/lineChange'} w={'14px'} color={'myGray.500'} />
|
||||||
|
</HStack>
|
||||||
|
</MyTooltip>
|
||||||
|
<Box w={'14rem'}>{isReference ? RenderReference : RenderInput}</Box>
|
||||||
|
</Flex>
|
||||||
|
|
||||||
|
{isDisabled && (
|
||||||
|
<Box
|
||||||
|
position="absolute"
|
||||||
|
top={0}
|
||||||
|
left={0}
|
||||||
|
right={0}
|
||||||
|
bottom={0}
|
||||||
|
bg="whiteAlpha.700"
|
||||||
|
zIndex={1}
|
||||||
|
borderRadius="sm"
|
||||||
|
cursor="not-allowed"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
@@ -3,7 +3,7 @@ import NodeCard from '../render/NodeCard';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||||
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
|
||||||
import { type NodeProps, Position, useViewport } from 'reactflow';
|
import { type NodeProps, Position } from 'reactflow';
|
||||||
import { type FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
import { type FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||||
import { type IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
import { type IfElseListItemType } from '@fastgpt/global/core/workflow/template/system/ifElse/type';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
@@ -14,11 +14,11 @@ import { MySourceHandle } from '../render/Handle';
|
|||||||
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
|
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
|
||||||
import ListItem from './ListItem';
|
import ListItem from './ListItem';
|
||||||
import { IfElseResultEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
import { IfElseResultEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
|
||||||
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
|
|
||||||
const NodeIfElse = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
const NodeIfElse = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { nodeId, inputs = [] } = data;
|
const { nodeId, inputs = [] } = data;
|
||||||
const { zoom } = useViewport();
|
|
||||||
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
|
||||||
const elseHandleId = getHandleId(nodeId, 'source', IfElseResultEnum.ELSE);
|
const elseHandleId = getHandleId(nodeId, 'source', IfElseResultEnum.ELSE);
|
||||||
|
|
||||||
@@ -108,6 +108,7 @@ const NodeIfElse = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
<Button
|
<Button
|
||||||
variant={'whiteBase'}
|
variant={'whiteBase'}
|
||||||
w={'full'}
|
w={'full'}
|
||||||
|
leftIcon={<MyIcon name={'common/addLight'} boxSize={4} mr={-1} />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
const ifElseListInput = inputs.find(
|
const ifElseListInput = inputs.find(
|
||||||
(input) => input.key === NodeInputKeyEnum.ifElseList
|
(input) => input.key === NodeInputKeyEnum.ifElseList
|
||||||
|
@@ -157,7 +157,7 @@ const OptionItem = ({
|
|||||||
<MyTooltip label={t('common:Delete')}>
|
<MyTooltip label={t('common:Delete')}>
|
||||||
<MyIcon
|
<MyIcon
|
||||||
mt={0.5}
|
mt={0.5}
|
||||||
name={'minus'}
|
name={'circleMinus'}
|
||||||
w={'0.8rem'}
|
w={'0.8rem'}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
color={'myGray.600'}
|
color={'myGray.600'}
|
||||||
|
@@ -249,7 +249,7 @@ const NodeVariableUpdate = ({ data, selected }: NodeProps<FlowNodeItemType>) =>
|
|||||||
if (valueType === WorkflowIOValueTypeEnum.number) {
|
if (valueType === WorkflowIOValueTypeEnum.number) {
|
||||||
return (
|
return (
|
||||||
<MyNumberInput
|
<MyNumberInput
|
||||||
bg={'white'}
|
inputFieldProps={{ bg: 'white' }}
|
||||||
value={Number(inputValue) || 0}
|
value={Number(inputValue) || 0}
|
||||||
onChange={(e) => onUpdateNewValue(String(e || 0))}
|
onChange={(e) => onUpdateNewValue(String(e || 0))}
|
||||||
/>
|
/>
|
||||||
|
@@ -13,7 +13,7 @@ const NumberInputRender = ({ item, nodeId }: RenderInputProps) => {
|
|||||||
value={item.value}
|
value={item.value}
|
||||||
min={item.min}
|
min={item.min}
|
||||||
max={item.max}
|
max={item.max}
|
||||||
bg={'white'}
|
inputFieldProps={{ bg: 'white' }}
|
||||||
rounded={'md'}
|
rounded={'md'}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
|
@@ -47,7 +47,7 @@ type CommonSelectProps = {
|
|||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
popDirection?: 'top' | 'bottom';
|
popDirection?: 'top' | 'bottom';
|
||||||
styles?: ButtonProps;
|
ButtonProps?: ButtonProps;
|
||||||
};
|
};
|
||||||
type SelectProps<T extends boolean> = CommonSelectProps & {
|
type SelectProps<T extends boolean> = CommonSelectProps & {
|
||||||
isArray?: T;
|
isArray?: T;
|
||||||
@@ -87,7 +87,7 @@ export const useReference = ({
|
|||||||
return {
|
return {
|
||||||
label: (
|
label: (
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Avatar src={node.avatar} w={isArray ? '1rem' : '1.25rem'} borderRadius={'xs'} />
|
<Avatar src={node.avatar} w={isArray ? '1rem' : '1.05rem'} borderRadius={'xs'} />
|
||||||
<Box ml={1}>{t(node.name as any)}</Box>
|
<Box ml={1}>{t(node.name as any)}</Box>
|
||||||
</Flex>
|
</Flex>
|
||||||
),
|
),
|
||||||
@@ -168,7 +168,8 @@ const SingleReferenceSelector = ({
|
|||||||
value,
|
value,
|
||||||
list = [],
|
list = [],
|
||||||
onSelect,
|
onSelect,
|
||||||
popDirection
|
popDirection,
|
||||||
|
ButtonProps
|
||||||
}: SelectProps<false>) => {
|
}: SelectProps<false>) => {
|
||||||
const getSelectValue = useCallback(
|
const getSelectValue = useCallback(
|
||||||
(value: ReferenceValueType) => {
|
(value: ReferenceValueType) => {
|
||||||
@@ -196,12 +197,10 @@ const SingleReferenceSelector = ({
|
|||||||
<MultipleRowSelect
|
<MultipleRowSelect
|
||||||
label={
|
label={
|
||||||
isValidSelect ? (
|
isValidSelect ? (
|
||||||
<Flex gap={2} alignItems={'center'} fontSize={'sm'}>
|
<Flex py={1} pl={1} alignItems={'center'} fontSize={'sm'}>
|
||||||
<Flex py={1} pl={1} alignItems={'center'}>
|
{nodeName}
|
||||||
{nodeName}
|
<MyIcon name={'common/rightArrowLight'} mx={0.5} w={'12px'} color={'myGray.500'} />
|
||||||
<MyIcon name={'common/rightArrowLight'} mx={1} w={'12px'} color={'myGray.500'} />
|
{outputName}
|
||||||
{outputName}
|
|
||||||
</Flex>
|
|
||||||
</Flex>
|
</Flex>
|
||||||
) : (
|
) : (
|
||||||
<Box fontSize={'sm'} color={'myGray.400'}>
|
<Box fontSize={'sm'} color={'myGray.400'}>
|
||||||
@@ -213,9 +212,10 @@ const SingleReferenceSelector = ({
|
|||||||
list={list}
|
list={list}
|
||||||
onSelect={onSelect as any}
|
onSelect={onSelect as any}
|
||||||
popDirection={popDirection}
|
popDirection={popDirection}
|
||||||
|
ButtonProps={ButtonProps}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [getSelectValue, list, onSelect, placeholder, popDirection, value]);
|
}, [ButtonProps, getSelectValue, list, onSelect, placeholder, popDirection, value]);
|
||||||
|
|
||||||
return ItemSelector;
|
return ItemSelector;
|
||||||
};
|
};
|
||||||
@@ -226,8 +226,6 @@ const MultipleReferenceSelector = ({
|
|||||||
onSelect,
|
onSelect,
|
||||||
popDirection
|
popDirection
|
||||||
}: SelectProps<true>) => {
|
}: SelectProps<true>) => {
|
||||||
const { t } = useTranslation();
|
|
||||||
|
|
||||||
const getSelectValue = useCallback(
|
const getSelectValue = useCallback(
|
||||||
(value: ReferenceValueType) => {
|
(value: ReferenceValueType) => {
|
||||||
if (!value) return [];
|
if (!value) return [];
|
||||||
|
@@ -269,7 +269,7 @@ const CollectionChunkForm = ({ form }: { form: UseFormReturn<CollectionChunkForm
|
|||||||
<Box flex={'1 0 0'}>
|
<Box flex={'1 0 0'}>
|
||||||
<MyNumberInput
|
<MyNumberInput
|
||||||
h={'34px'}
|
h={'34px'}
|
||||||
bg={'white'}
|
inputFieldProps={{ bg: 'white' }}
|
||||||
min={100}
|
min={100}
|
||||||
max={100000}
|
max={100000}
|
||||||
register={register}
|
register={register}
|
||||||
|
@@ -20,6 +20,3 @@ export const submitInvoice = (data: InvoiceType) =>
|
|||||||
|
|
||||||
export const getInvoiceRecords = (data: PaginationProps) =>
|
export const getInvoiceRecords = (data: PaginationProps) =>
|
||||||
POST<PaginationResponse<InvoiceSchemaType>>(`/proApi/support/wallet/bill/invoice/records`, data);
|
POST<PaginationResponse<InvoiceSchemaType>>(`/proApi/support/wallet/bill/invoice/records`, data);
|
||||||
|
|
||||||
export const readInvoiceFile = (id: string) =>
|
|
||||||
GET<InvoiceFileInfo>(`/proApi/support/wallet/bill/invoice/file/read`, { id });
|
|
||||||
|
Reference in New Issue
Block a user