diff --git a/packages/global/core/workflow/node/constant.ts b/packages/global/core/workflow/node/constant.ts index 11d8b395d..af4aa98b7 100644 --- a/packages/global/core/workflow/node/constant.ts +++ b/packages/global/core/workflow/node/constant.ts @@ -7,6 +7,7 @@ export enum FlowNodeInputTypeEnum { // render ui numberInput = 'numberInput', switch = 'switch', // true/false select = 'select', + multipleSelect = 'multipleSelect', // editor JSONEditor = 'JSONEditor', @@ -46,6 +47,9 @@ export const FlowNodeInputMap: Record< [FlowNodeInputTypeEnum.select]: { icon: 'core/workflow/inputType/option' }, + [FlowNodeInputTypeEnum.multipleSelect]: { + icon: 'core/workflow/inputType/option' + }, [FlowNodeInputTypeEnum.switch]: { icon: 'core/workflow/inputType/switch' }, diff --git a/packages/web/components/common/MySelect/MultipleSelect.tsx b/packages/web/components/common/MySelect/MultipleSelect.tsx index cdf451772..e8888fac4 100644 --- a/packages/web/components/common/MySelect/MultipleSelect.tsx +++ b/packages/web/components/common/MySelect/MultipleSelect.tsx @@ -1,5 +1,6 @@ import { Box, + Button, ButtonProps, Checkbox, Flex, @@ -26,13 +27,14 @@ export type SelectProps = { }[]; value: T[]; isSelectAll: boolean; - setIsSelectAll: React.Dispatch>; + setIsSelectAll?: React.Dispatch>; placeholder?: string; maxH?: number; itemWrap?: boolean; onSelect: (val: T[]) => void; closeable?: boolean; + isDisabled?: boolean; ScrollData?: ReturnType['ScrollData']; } & Omit; @@ -47,6 +49,7 @@ const MultipleSelect = ({ ScrollData, isSelectAll, setIsSelectAll, + isDisabled = false, ...props }: SelectProps) => { const ref = useRef(null); @@ -70,7 +73,7 @@ const MultipleSelect = ({ // 全选状态下,value 实际上上空。 if (isSelectAll) { onSelect(list.map((item) => item.value).filter((i) => i !== val)); - setIsSelectAll(false); + setIsSelectAll?.(false); return; } @@ -87,7 +90,7 @@ const MultipleSelect = ({ const hasSelected = isSelectAll || value.length > 0; onSelect(hasSelected ? [] : list.map((item) => item.value)); - setIsSelectAll((state) => !state); + setIsSelectAll?.((state) => !state); }, [value, list, setIsSelectAll, onSelect]); const ListRender = useMemo(() => { @@ -126,11 +129,11 @@ const MultipleSelect = ({ }, [value, list, isSelectAll]); return ( - + ({ > ); + case FlowNodeInputTypeEnum.multipleSelect: + return ( + { + if (!list) return <>; + return ( + + width={'100%'} + bg={'white'} + py={2} + list={list} + value={value} + isDisabled={submitted} + onSelect={(e) => setValue(label, e)} + isSelectAll={value.length === list.length} + /> + ); + }} + /> + ); default: return null; } diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeFormInput/InputFormEditModal.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeFormInput/InputFormEditModal.tsx index 4b20cfa5c..7c20e7e32 100644 --- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeFormInput/InputFormEditModal.tsx +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodeFormInput/InputFormEditModal.tsx @@ -64,6 +64,12 @@ const InputFormEditModal = ({ label: t('common:core.workflow.inputType.select'), value: FlowNodeInputTypeEnum.select, defaultValueType: WorkflowIOValueTypeEnum.string + }, + { + icon: 'core/workflow/inputType/option', + label: t('workflow:input_type_multiple_select'), + value: FlowNodeInputTypeEnum.multipleSelect, + defaultValueType: WorkflowIOValueTypeEnum.arrayString } ]; diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig.tsx index bb4448003..80bc584be 100644 --- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig.tsx +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/NodePluginIO/InputTypeConfig.tsx @@ -144,6 +144,7 @@ const InputTypeConfig = ({ FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.switch, FlowNodeInputTypeEnum.select, + FlowNodeInputTypeEnum.multipleSelect, VariableInputEnum.custom ]; @@ -157,7 +158,8 @@ const InputTypeConfig = ({ FlowNodeInputTypeEnum.input, FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.switch, - FlowNodeInputTypeEnum.select + FlowNodeInputTypeEnum.select, + FlowNodeInputTypeEnum.multipleSelect ]; return type === 'plugin' && list.includes(inputType as FlowNodeInputTypeEnum); }, [inputType, type]); @@ -363,6 +365,27 @@ const InputTypeConfig = ({ w={'200px'} /> )} + {inputType === FlowNodeInputTypeEnum.multipleSelect && ( + + flex={'1 0 0'} + itemWrap={true} + bg={'myGray.50'} + list={listValue + .filter((item: any) => item.label !== '') + .map((item: any) => ({ + label: item.label, + value: item.value + }))} + placeholder={t('workflow:select_default_option')} + value={defaultValue || []} + onSelect={(val) => setValue('defaultValue', val)} + isSelectAll={ + defaultValue && + defaultValue.length === + listValue.filter((item: any) => item.label !== '').length + } + /> + )} )} @@ -390,7 +413,8 @@ const InputTypeConfig = ({ )} - {inputType === FlowNodeInputTypeEnum.select && ( + {(inputType === FlowNodeInputTypeEnum.select || + inputType == FlowNodeInputTypeEnum.multipleSelect) && ( <> onDragEndCb={(list) => { diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx index d4c4e9017..2fc23d22d 100644 --- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/index.tsx @@ -25,6 +25,9 @@ const RenderList: Record< [FlowNodeInputTypeEnum.select]: { Component: dynamic(() => import('./templates/Select')) }, + [FlowNodeInputTypeEnum.multipleSelect]: { + Component: dynamic(() => import('./templates/SelectMulti')) + }, [FlowNodeInputTypeEnum.numberInput]: { Component: dynamic(() => import('./templates/NumberInput')) }, diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectMulti.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectMulti.tsx new file mode 100644 index 000000000..5c30b45d0 --- /dev/null +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/RenderInput/templates/SelectMulti.tsx @@ -0,0 +1,43 @@ +import MultipleSelect from '@fastgpt/web/components/common/MySelect/MultipleSelect'; +import { RenderInputProps } from '../type'; +import { WorkflowContext } from '@/pageComponents/app/detail/WorkflowComponents/context'; +import { useContextSelector } from 'use-context-selector'; +import React from 'react'; + +const SelectMultiRender = ({ item, nodeId }: RenderInputProps) => { + const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode); + return ( + + width={'100%'} + value={item.value} + list={item.list || []} + onSelect={(e) => + onChangeNode({ + nodeId, + type: 'updateInput', + key: item.key, + value: { + ...item, + value: e + } + }) + } + isSelectAll={item.value.length === item?.list?.length} + setIsSelectAll={(all) => { + if (all) { + onChangeNode({ + nodeId, + type: 'updateInput', + key: item.key, + value: { + ...item, + value: item?.list?.map((item) => item.value) + } + }); + } + }} + /> + ); +}; + +export default React.memo(SelectMultiRender);