loop node dynamic height (#3092)
* loop node dynamic height * fix * fix
This commit is contained in:
@@ -5,7 +5,7 @@ import { StoreNodeItemType } from '../type/node';
|
|||||||
import { StoreEdgeItemType } from '../type/edge';
|
import { StoreEdgeItemType } from '../type/edge';
|
||||||
import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
|
import { RuntimeEdgeItemType, RuntimeNodeItemType } from './type';
|
||||||
import { VARIABLE_NODE_ID } from '../constants';
|
import { VARIABLE_NODE_ID } from '../constants';
|
||||||
import { isReferenceValue, isReferenceValueArray } from '../utils';
|
import { isReferenceValueFormat } from '../utils';
|
||||||
import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
||||||
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
||||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
||||||
@@ -244,7 +244,7 @@ export const getReferenceVariableValue = ({
|
|||||||
const nodeIds = nodes.map((node) => node.nodeId);
|
const nodeIds = nodes.map((node) => node.nodeId);
|
||||||
|
|
||||||
// handle single reference value
|
// handle single reference value
|
||||||
if (isReferenceValue(value, nodeIds)) {
|
if (isReferenceValueFormat(value)) {
|
||||||
const sourceNodeId = value[0];
|
const sourceNodeId = value[0];
|
||||||
const outputId = value[1];
|
const outputId = value[1];
|
||||||
|
|
||||||
@@ -261,7 +261,11 @@ export const getReferenceVariableValue = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// handle reference array
|
// handle reference array
|
||||||
if (isReferenceValueArray(value, nodeIds)) {
|
if (
|
||||||
|
Array.isArray(value) &&
|
||||||
|
value.length > 0 &&
|
||||||
|
value.every((item) => isReferenceValueFormat(item))
|
||||||
|
) {
|
||||||
const result = value.map<any>((val) => {
|
const result = value.map<any>((val) => {
|
||||||
return getReferenceVariableValue({
|
return getReferenceVariableValue({
|
||||||
value: val,
|
value: val,
|
||||||
@@ -270,7 +274,7 @@ export const getReferenceVariableValue = ({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return result.flat();
|
return result.flat().filter((item) => item !== undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export const Input_Template_Node_Height: FlowNodeInputItemType = {
|
|||||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||||
valueType: WorkflowIOValueTypeEnum.number,
|
valueType: WorkflowIOValueTypeEnum.number,
|
||||||
label: '',
|
label: '',
|
||||||
value: 960
|
value: 600
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Input_Template_Stream_MODE: FlowNodeInputItemType = {
|
export const Input_Template_Stream_MODE: FlowNodeInputItemType = {
|
||||||
|
|||||||
@@ -306,6 +306,15 @@ export const isReferenceValue = (value: any, nodeIds: string[]): value is [strin
|
|||||||
const validIdSet = new Set([VARIABLE_NODE_ID, ...nodeIds]);
|
const validIdSet = new Set([VARIABLE_NODE_ID, ...nodeIds]);
|
||||||
return validIdSet.has(value[0]);
|
return validIdSet.has(value[0]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isReferenceValueFormat = (value: any): value is [string, string] => {
|
||||||
|
return (
|
||||||
|
Array.isArray(value) &&
|
||||||
|
value.length === 2 &&
|
||||||
|
typeof value[0] === 'string' &&
|
||||||
|
typeof value[1] === 'string'
|
||||||
|
);
|
||||||
|
};
|
||||||
export const isReferenceValueArray = (
|
export const isReferenceValueArray = (
|
||||||
value: any,
|
value: any,
|
||||||
nodeIds: string[]
|
nodeIds: string[]
|
||||||
|
|||||||
@@ -501,7 +501,7 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
|
|||||||
nodes: runtimeNodes,
|
nodes: runtimeNodes,
|
||||||
variables
|
variables
|
||||||
});
|
});
|
||||||
console.log(value, '=-=-');
|
|
||||||
// Dynamic input is stored in the dynamic key
|
// Dynamic input is stored in the dynamic key
|
||||||
if (input.canEdit && dynamicInput && params[dynamicInput.key]) {
|
if (input.canEdit && dynamicInput && params[dynamicInput.key]) {
|
||||||
params[dynamicInput.key][input.key] = valueTypeFormat(value, input.valueType);
|
params[dynamicInput.key][input.key] = valueTypeFormat(value, input.valueType);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useEffect, useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
Background,
|
Background,
|
||||||
ControlButton,
|
ControlButton,
|
||||||
@@ -46,17 +46,24 @@ const FlowController = React.memo(function FlowController() {
|
|||||||
|
|
||||||
const isMac = !window ? false : window.navigator.userAgent.toLocaleLowerCase().includes('mac');
|
const isMac = !window ? false : window.navigator.userAgent.toLocaleLowerCase().includes('mac');
|
||||||
|
|
||||||
// Controller shortcut key
|
useKeyPress(['ctrl.z', 'meta.z', 'ctrl.shift.z', 'meta.shift.z', 'ctrl.y', 'meta.y'], (e) => {
|
||||||
useKeyPress(['ctrl.z', 'meta.z'], (e) => {
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
if (!mouseInCanvas) return;
|
if (!mouseInCanvas) return;
|
||||||
undo();
|
|
||||||
});
|
const isUndo = e.key.toLowerCase() === 'z' && !e.shiftKey;
|
||||||
useKeyPress(['ctrl.shift.z', 'meta.shift.z', 'ctrl.y', 'meta.y'], (e) => {
|
const isRedo = (e.key.toLowerCase() === 'z' && e.shiftKey) || e.key.toLowerCase() === 'y';
|
||||||
if (!mouseInCanvas) return;
|
|
||||||
redo();
|
if (isUndo) {
|
||||||
|
undo();
|
||||||
|
} else if (isRedo) {
|
||||||
|
redo();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useKeyPress(['ctrl.add', 'meta.add', 'ctrl.equalsign', 'meta.equalsign'], (e) => {
|
useKeyPress(['ctrl.add', 'meta.add', 'ctrl.equalsign', 'meta.equalsign'], (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
if (!mouseInCanvas) return;
|
if (!mouseInCanvas) return;
|
||||||
zoomIn();
|
zoomIn();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -293,9 +293,24 @@ export const useWorkflow = () => {
|
|||||||
const { isDowningCtrl } = useKeyboard();
|
const { isDowningCtrl } = useKeyboard();
|
||||||
|
|
||||||
// Loop node size and position
|
// Loop node size and position
|
||||||
const resetParentNodeSizeAndPosition = useMemoizedFn((rect: Rect, parentId: string) => {
|
const resetParentNodeSizeAndPosition = useMemoizedFn((parentId: string) => {
|
||||||
const width = rect.width + 110 > 900 ? rect.width + 110 : 900;
|
const { childNodes, loopNode } = nodes.reduce(
|
||||||
const height = rect.height + 420 > 900 ? rect.height + 420 : 900;
|
(acc, node) => {
|
||||||
|
if (node.data.parentNodeId === parentId) {
|
||||||
|
acc.childNodes.push(node);
|
||||||
|
}
|
||||||
|
if (node.id === parentId) {
|
||||||
|
acc.loopNode = node;
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ childNodes: [] as Node[], loopNode: undefined as Node | undefined }
|
||||||
|
);
|
||||||
|
const rect = getNodesBounds(childNodes);
|
||||||
|
|
||||||
|
// Calculate parent node size with minimum width/height constraints
|
||||||
|
const width = Math.max(rect.width + 80, 840);
|
||||||
|
const height = Math.max(rect.height + 80, 600);
|
||||||
|
|
||||||
// Update parentNode size and position
|
// Update parentNode size and position
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
@@ -317,14 +332,17 @@ export const useWorkflow = () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Calculate position offset
|
||||||
|
const offsetHeight = loopNode?.height ? loopNode.height - height - 380 : 0;
|
||||||
|
|
||||||
// Update parentNode position
|
// Update parentNode position
|
||||||
onNodesChange([
|
onNodesChange([
|
||||||
{
|
{
|
||||||
id: parentId,
|
id: parentId,
|
||||||
type: 'position',
|
type: 'position',
|
||||||
position: {
|
position: {
|
||||||
x: rect.x - 50,
|
x: rect.x - 70,
|
||||||
y: rect.y - 300
|
y: rect.y - (320 + offsetHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
@@ -335,43 +353,41 @@ export const useWorkflow = () => {
|
|||||||
const [helperLineVertical, setHelperLineVertical] = useState<THelperLine>();
|
const [helperLineVertical, setHelperLineVertical] = useState<THelperLine>();
|
||||||
|
|
||||||
const checkNodeHelpLine = useMemoizedFn((change: NodeChange, nodes: Node[]) => {
|
const checkNodeHelpLine = useMemoizedFn((change: NodeChange, nodes: Node[]) => {
|
||||||
requestAnimationFrame(() => {
|
const positionChange = change.type === 'position' && change.dragging ? change : undefined;
|
||||||
const positionChange = change.type === 'position' && change.dragging ? change : undefined;
|
|
||||||
|
|
||||||
if (positionChange?.position) {
|
if (positionChange?.position) {
|
||||||
// 只判断,3000px 内的 nodes,并按从近到远的顺序排序
|
// 只判断,3000px 内的 nodes,并按从近到远的顺序排序
|
||||||
const filterNodes = nodes
|
const filterNodes = nodes
|
||||||
.filter((node) => {
|
.filter((node) => {
|
||||||
if (!positionChange.position) return false;
|
if (!positionChange.position) return false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
Math.abs(node.position.x - positionChange.position.x) <= 3000 &&
|
Math.abs(node.position.x - positionChange.position.x) <= 3000 &&
|
||||||
Math.abs(node.position.y - positionChange.position.y) <= 3000
|
Math.abs(node.position.y - positionChange.position.y) <= 3000
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (!positionChange.position) return 0;
|
if (!positionChange.position) return 0;
|
||||||
return (
|
return (
|
||||||
Math.abs(a.position.x - positionChange.position.x) +
|
Math.abs(a.position.x - positionChange.position.x) +
|
||||||
Math.abs(a.position.y - positionChange.position.y) -
|
Math.abs(a.position.y - positionChange.position.y) -
|
||||||
Math.abs(b.position.x - positionChange.position.x) -
|
Math.abs(b.position.x - positionChange.position.x) -
|
||||||
Math.abs(b.position.y - positionChange.position.y)
|
Math.abs(b.position.y - positionChange.position.y)
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.slice(0, 15);
|
.slice(0, 15);
|
||||||
|
|
||||||
const helperLines = computeHelperLines(positionChange, filterNodes);
|
const helperLines = computeHelperLines(positionChange, filterNodes);
|
||||||
|
|
||||||
positionChange.position.x = helperLines.snapPosition.x ?? positionChange.position.x;
|
positionChange.position.x = helperLines.snapPosition.x ?? positionChange.position.x;
|
||||||
positionChange.position.y = helperLines.snapPosition.y ?? positionChange.position.y;
|
positionChange.position.y = helperLines.snapPosition.y ?? positionChange.position.y;
|
||||||
|
|
||||||
setHelperLineHorizontal(helperLines.horizontal);
|
setHelperLineHorizontal(helperLines.horizontal);
|
||||||
setHelperLineVertical(helperLines.vertical);
|
setHelperLineVertical(helperLines.vertical);
|
||||||
} else {
|
} else {
|
||||||
setHelperLineHorizontal(undefined);
|
setHelperLineHorizontal(undefined);
|
||||||
setHelperLineVertical(undefined);
|
setHelperLineVertical(undefined);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check if a node is placed on top of a loop node
|
// Check if a node is placed on top of a loop node
|
||||||
@@ -412,9 +428,7 @@ export const useWorkflow = () => {
|
|||||||
state.filter((edge) => edge.source !== node.id && edge.target !== node.id)
|
state.filter((edge) => edge.source !== node.id && edge.target !== node.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
const childNodes = [...nodes.filter((n) => n.data.parentNodeId === parentNode.id), node];
|
resetParentNodeSizeAndPosition(parentNode.id);
|
||||||
const rect = getNodesBounds(childNodes);
|
|
||||||
resetParentNodeSizeAndPosition(rect, parentNode.id);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -469,7 +483,7 @@ export const useWorkflow = () => {
|
|||||||
const childNodes = nodes.filter((n) => n.data.parentNodeId === parentId);
|
const childNodes = nodes.filter((n) => n.data.parentNodeId === parentId);
|
||||||
checkNodeHelpLine(change, childNodes);
|
checkNodeHelpLine(change, childNodes);
|
||||||
|
|
||||||
resetParentNodeSizeAndPosition(getNodesBounds(childNodes), parentId);
|
resetParentNodeSizeAndPosition(parentId);
|
||||||
}
|
}
|
||||||
// If node is parent node, move parent node and child nodes
|
// If node is parent node, move parent node and child nodes
|
||||||
else if (parentNode[node.data.flowNodeType]) {
|
else if (parentNode[node.data.flowNodeType]) {
|
||||||
@@ -679,7 +693,8 @@ export const useWorkflow = () => {
|
|||||||
helperLineVertical,
|
helperLineVertical,
|
||||||
onNodeDragStop,
|
onNodeDragStop,
|
||||||
onPaneContextMenu,
|
onPaneContextMenu,
|
||||||
onPaneClick
|
onPaneClick,
|
||||||
|
resetParentNodeSizeAndPosition
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
|
||||||
import React, { useEffect, useMemo } from 'react';
|
import React, { useEffect, useMemo, useRef, useCallback } from 'react';
|
||||||
import { Background, NodeProps } from 'reactflow';
|
import { Background, NodeProps } from 'reactflow';
|
||||||
import NodeCard from '../render/NodeCard';
|
import NodeCard from '../render/NodeCard';
|
||||||
import Container from '../../components/Container';
|
import Container from '../../components/Container';
|
||||||
@@ -24,17 +24,18 @@ import {
|
|||||||
import { Input_Template_Children_Node_List } from '@fastgpt/global/core/workflow/template/input';
|
import { Input_Template_Children_Node_List } from '@fastgpt/global/core/workflow/template/input';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext } from '../../../context';
|
import { WorkflowContext } from '../../../context';
|
||||||
import { cloneDeep } from 'lodash';
|
|
||||||
import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
import { getWorkflowGlobalVariables } from '@/web/core/workflow/utils';
|
||||||
import { AppContext } from '../../../../context';
|
import { AppContext } from '../../../../context';
|
||||||
import { isReferenceValue, isReferenceValueArray } from '@fastgpt/global/core/workflow/utils';
|
import { isReferenceValue, isReferenceValueArray } from '@fastgpt/global/core/workflow/utils';
|
||||||
import { ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io';
|
import { ReferenceItemValueType } from '@fastgpt/global/core/workflow/type/io';
|
||||||
|
import { useWorkflow } from '../../hooks/useWorkflow';
|
||||||
|
|
||||||
const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { nodeId, inputs, outputs, isFolded } = data;
|
const { nodeId, inputs, outputs, isFolded } = data;
|
||||||
const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v);
|
const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v);
|
||||||
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
const { appDetail } = useContextSelector(AppContext, (v) => v);
|
||||||
|
const { resetParentNodeSizeAndPosition } = useWorkflow();
|
||||||
|
|
||||||
const loopInputArray = inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray);
|
const loopInputArray = inputs.find((input) => input.key === NodeInputKeyEnum.loopInputArray);
|
||||||
|
|
||||||
@@ -44,6 +45,7 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
nodeHeight: inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value
|
nodeHeight: inputs.find((input) => input.key === NodeInputKeyEnum.nodeHeight)?.value
|
||||||
};
|
};
|
||||||
}, [inputs]);
|
}, [inputs]);
|
||||||
|
|
||||||
const childrenNodeIdList = useMemo(() => {
|
const childrenNodeIdList = useMemo(() => {
|
||||||
return JSON.stringify(
|
return JSON.stringify(
|
||||||
nodeList.filter((node) => node.parentNodeId === nodeId).map((node) => node.nodeId)
|
nodeList.filter((node) => node.parentNodeId === nodeId).map((node) => node.nodeId)
|
||||||
@@ -84,6 +86,7 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
|
|
||||||
return type ?? WorkflowIOValueTypeEnum.arrayAny;
|
return type ?? WorkflowIOValueTypeEnum.arrayAny;
|
||||||
}, [appDetail.chatConfig, loopInputArray, nodeList]);
|
}, [appDetail.chatConfig, loopInputArray, nodeList]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loopInputArray) return;
|
if (!loopInputArray) return;
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
@@ -110,20 +113,15 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
});
|
});
|
||||||
}, [childrenNodeIdList, nodeId, onChangeNode]);
|
}, [childrenNodeIdList, nodeId, onChangeNode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
resetParentNodeSizeAndPosition(nodeId);
|
||||||
|
}, 0);
|
||||||
|
}, [loopInputArray, nodeId, resetParentNodeSizeAndPosition]);
|
||||||
|
|
||||||
const Render = useMemo(() => {
|
const Render = useMemo(() => {
|
||||||
return (
|
return (
|
||||||
<NodeCard
|
<NodeCard selected={selected} maxW="full" menuForbid={{ copy: true }} {...data}>
|
||||||
selected={selected}
|
|
||||||
maxW="full"
|
|
||||||
{...(!isFolded && {
|
|
||||||
minW: '900px',
|
|
||||||
minH: '900px',
|
|
||||||
w: nodeWidth,
|
|
||||||
h: nodeHeight
|
|
||||||
})}
|
|
||||||
menuForbid={{ copy: true }}
|
|
||||||
{...data}
|
|
||||||
>
|
|
||||||
<Container position={'relative'} flex={1}>
|
<Container position={'relative'} flex={1}>
|
||||||
<IOTitle text={t('common:common.Input')} />
|
<IOTitle text={t('common:common.Input')} />
|
||||||
<Box mb={6} maxW={'500px'}>
|
<Box mb={6} maxW={'500px'}>
|
||||||
@@ -132,7 +130,17 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
|
|||||||
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
|
<FormLabel required fontWeight={'medium'} mb={3} color={'myGray.600'}>
|
||||||
{t('workflow:loop_body')}
|
{t('workflow:loop_body')}
|
||||||
</FormLabel>
|
</FormLabel>
|
||||||
<Box flex={1} position={'relative'} border={'base'} bg={'myGray.100'} rounded={'8px'}>
|
<Box
|
||||||
|
flex={1}
|
||||||
|
position={'relative'}
|
||||||
|
border={'base'}
|
||||||
|
bg={'myGray.100'}
|
||||||
|
rounded={'8px'}
|
||||||
|
{...(!isFolded && {
|
||||||
|
minW: nodeWidth,
|
||||||
|
minH: nodeHeight
|
||||||
|
})}
|
||||||
|
>
|
||||||
<Background />
|
<Background />
|
||||||
</Box>
|
</Box>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
@@ -145,14 +145,25 @@ function Reference({
|
|||||||
const onUpdateField = useCallback(
|
const onUpdateField = useCallback(
|
||||||
({ data }: { data: FlowNodeInputItemType }) => {
|
({ data }: { data: FlowNodeInputItemType }) => {
|
||||||
if (!data.key) return;
|
if (!data.key) return;
|
||||||
|
const oldType = inputChildren.valueType;
|
||||||
|
const newType = data.valueType;
|
||||||
|
let newValue = data.value;
|
||||||
|
if (oldType?.includes('array') && !newType?.includes('array')) {
|
||||||
|
newValue = data.value[0];
|
||||||
|
} else if (!oldType?.includes('array') && newType?.includes('array')) {
|
||||||
|
newValue = [data.value];
|
||||||
|
}
|
||||||
|
|
||||||
onChangeNode({
|
onChangeNode({
|
||||||
nodeId,
|
nodeId,
|
||||||
type: 'replaceInput',
|
type: 'replaceInput',
|
||||||
key: inputChildren.key,
|
key: inputChildren.key,
|
||||||
value: {
|
value: {
|
||||||
...data,
|
...inputChildren,
|
||||||
value: data
|
value: newValue,
|
||||||
|
key: data.key,
|
||||||
|
label: data.label,
|
||||||
|
valueType: data.valueType
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import React, { useCallback, useMemo } from 'react';
|
|||||||
import type { RenderInputProps } from '../type';
|
import type { RenderInputProps } from '../type';
|
||||||
import { Flex, Box, ButtonProps, Grid } from '@chakra-ui/react';
|
import { Flex, Box, ButtonProps, Grid } from '@chakra-ui/react';
|
||||||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||||||
import { computedNodeInputReference } from '@/web/core/workflow/utils';
|
import {
|
||||||
|
computedNodeInputReference,
|
||||||
|
filterWorkflowNodeOutputsByType
|
||||||
|
} from '@/web/core/workflow/utils';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import {
|
import {
|
||||||
NodeOutputKeyEnum,
|
NodeOutputKeyEnum,
|
||||||
@@ -77,7 +80,6 @@ export const useReference = ({
|
|||||||
if (!sourceNodes) return [];
|
if (!sourceNodes) return [];
|
||||||
|
|
||||||
const isArray = valueType?.includes('array');
|
const isArray = valueType?.includes('array');
|
||||||
const arrayItemType = isArray ? valueType.replace('array', '').toLowerCase() : valueType;
|
|
||||||
|
|
||||||
// 转换为 select 的数据结构
|
// 转换为 select 的数据结构
|
||||||
const list: CommonSelectProps['list'] = sourceNodes
|
const list: CommonSelectProps['list'] = sourceNodes
|
||||||
@@ -90,16 +92,7 @@ export const useReference = ({
|
|||||||
</Flex>
|
</Flex>
|
||||||
),
|
),
|
||||||
value: node.nodeId,
|
value: node.nodeId,
|
||||||
children: node.outputs
|
children: filterWorkflowNodeOutputsByType(node.outputs, valueType)
|
||||||
.filter(
|
|
||||||
(output) =>
|
|
||||||
valueType === WorkflowIOValueTypeEnum.any ||
|
|
||||||
valueType === WorkflowIOValueTypeEnum.arrayAny ||
|
|
||||||
output.valueType === WorkflowIOValueTypeEnum.any ||
|
|
||||||
output.valueType === valueType ||
|
|
||||||
// Array<String> can select string
|
|
||||||
arrayItemType === output.valueType
|
|
||||||
)
|
|
||||||
.filter((output) => output.id !== NodeOutputKeyEnum.addOutputParam)
|
.filter((output) => output.id !== NodeOutputKeyEnum.addOutputParam)
|
||||||
.map((output) => {
|
.map((output) => {
|
||||||
return {
|
return {
|
||||||
@@ -199,7 +192,7 @@ const SingleReferenceSelector = ({
|
|||||||
label={
|
label={
|
||||||
isValidSelect ? (
|
isValidSelect ? (
|
||||||
<Flex gap={2} alignItems={'center'} fontSize={'sm'}>
|
<Flex gap={2} alignItems={'center'} fontSize={'sm'}>
|
||||||
<Flex py={1} pl={1}>
|
<Flex py={1} pl={1} alignItems={'center'}>
|
||||||
{nodeName}
|
{nodeName}
|
||||||
<MyIcon name={'common/rightArrowLight'} mx={1} w={'12px'} color={'myGray.500'} />
|
<MyIcon name={'common/rightArrowLight'} mx={1} w={'12px'} color={'myGray.500'} />
|
||||||
{outputName}
|
{outputName}
|
||||||
@@ -249,20 +242,14 @@ const MultipleReferenceSelector = ({
|
|||||||
|
|
||||||
const ArraySelector = useMemo(() => {
|
const ArraySelector = useMemo(() => {
|
||||||
const selectorVal = value as ReferenceItemValueType[];
|
const selectorVal = value as ReferenceItemValueType[];
|
||||||
const notValidItem =
|
const validSelectValue = selectorVal && selectorVal.length > 0;
|
||||||
!selectorVal ||
|
|
||||||
selectorVal.length === 0 ||
|
|
||||||
selectorVal.every((item) => {
|
|
||||||
const [nodeName, outputName] = getSelectValue(item);
|
|
||||||
return !nodeName || !outputName;
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MultipleRowArraySelect
|
<MultipleRowArraySelect
|
||||||
label={
|
label={
|
||||||
!notValidItem ? (
|
validSelectValue ? (
|
||||||
<Grid py={3} gridTemplateColumns={'1fr 1fr'} gap={2} fontSize={'sm'}>
|
<Grid py={3} gridTemplateColumns={'1fr 1fr'} gap={2} fontSize={'sm'}>
|
||||||
{selectorVal.map((item, index) => {
|
{selectorVal?.map((item, index) => {
|
||||||
const [nodeName, outputName] = getSelectValue(item);
|
const [nodeName, outputName] = getSelectValue(item);
|
||||||
const isInvalidItem = !nodeName || !outputName;
|
const isInvalidItem = !nodeName || !outputName;
|
||||||
|
|
||||||
@@ -270,8 +257,8 @@ const MultipleReferenceSelector = ({
|
|||||||
<Flex
|
<Flex
|
||||||
alignItems={'center'}
|
alignItems={'center'}
|
||||||
key={index}
|
key={index}
|
||||||
bg={'primary.50'}
|
bg={isInvalidItem ? 'red.50' : 'primary.50'}
|
||||||
color={'myGray.900'}
|
color={isInvalidItem ? 'red.500' : 'myGray.900'}
|
||||||
py={1}
|
py={1}
|
||||||
px={1.5}
|
px={1.5}
|
||||||
rounded={'sm'}
|
rounded={'sm'}
|
||||||
@@ -282,21 +269,27 @@ const MultipleReferenceSelector = ({
|
|||||||
maxW={'200px'}
|
maxW={'200px'}
|
||||||
className="textEllipsis"
|
className="textEllipsis"
|
||||||
>
|
>
|
||||||
{nodeName}
|
{isInvalidItem ? (
|
||||||
<MyIcon
|
<>{t('common:invalid_variable')}</>
|
||||||
name={'common/rightArrowLight'}
|
) : (
|
||||||
mx={1}
|
<>
|
||||||
w={'12px'}
|
{nodeName}
|
||||||
color={'myGray.500'}
|
<MyIcon
|
||||||
/>
|
name={'common/rightArrowLight'}
|
||||||
{outputName}
|
mx={1}
|
||||||
|
w={'12px'}
|
||||||
|
color={'myGray.500'}
|
||||||
|
/>
|
||||||
|
{outputName}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</Flex>
|
</Flex>
|
||||||
<MyIcon
|
<MyIcon
|
||||||
name={'common/closeLight'}
|
name={'common/closeLight'}
|
||||||
w={'1rem'}
|
w={'1rem'}
|
||||||
ml={1}
|
ml={1}
|
||||||
cursor={'pointer'}
|
cursor={'pointer'}
|
||||||
color={'myGray.500'}
|
color={isInvalidItem ? 'red.500' : 'myGray.500'}
|
||||||
_hover={{
|
_hover={{
|
||||||
color: 'red.600'
|
color: 'red.600'
|
||||||
}}
|
}}
|
||||||
@@ -321,7 +314,7 @@ const MultipleReferenceSelector = ({
|
|||||||
popDirection={popDirection}
|
popDirection={popDirection}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}, [getSelectValue, list, onSelect, placeholder, popDirection, value]);
|
}, [getSelectValue, list, onSelect, placeholder, popDirection, t, value]);
|
||||||
|
|
||||||
return ArraySelector;
|
return ArraySelector;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -355,7 +355,6 @@ const WorkflowContextProvider = ({
|
|||||||
const getNodes = useContextSelector(WorkflowActionContext, (state) => state.getNodes);
|
const getNodes = useContextSelector(WorkflowActionContext, (state) => state.getNodes);
|
||||||
const nodeListString = useContextSelector(WorkflowActionContext, (state) => state.nodeListString);
|
const nodeListString = useContextSelector(WorkflowActionContext, (state) => state.nodeListString);
|
||||||
|
|
||||||
console.log(121211111111);
|
|
||||||
const nodeList = useMemo(
|
const nodeList = useMemo(
|
||||||
() => JSON.parse(nodeListString) as FlowNodeItemType[],
|
() => JSON.parse(nodeListString) as FlowNodeItemType[],
|
||||||
[nodeListString]
|
[nodeListString]
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ import {
|
|||||||
getAppChatConfig,
|
getAppChatConfig,
|
||||||
getGuideModule,
|
getGuideModule,
|
||||||
isReferenceValue,
|
isReferenceValue,
|
||||||
isReferenceValueArray
|
isReferenceValueFormat
|
||||||
} from '@fastgpt/global/core/workflow/utils';
|
} from '@fastgpt/global/core/workflow/utils';
|
||||||
import { TFunction } from 'next-i18next';
|
import { TFunction } from 'next-i18next';
|
||||||
import {
|
import {
|
||||||
@@ -263,6 +263,20 @@ export const getRefData = ({
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const filterWorkflowNodeOutputsByType = (
|
||||||
|
outputs: FlowNodeOutputItemType[],
|
||||||
|
valueType: WorkflowIOValueTypeEnum
|
||||||
|
): FlowNodeOutputItemType[] => {
|
||||||
|
return outputs.filter(
|
||||||
|
(output) =>
|
||||||
|
valueType === WorkflowIOValueTypeEnum.any ||
|
||||||
|
valueType === WorkflowIOValueTypeEnum.arrayAny ||
|
||||||
|
output.valueType === WorkflowIOValueTypeEnum.any ||
|
||||||
|
output.valueType === valueType ||
|
||||||
|
valueType?.replace('array', '').toLowerCase() === output.valueType
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/* Connection rules */
|
/* Connection rules */
|
||||||
export const checkWorkflowNodeAndConnection = ({
|
export const checkWorkflowNodeAndConnection = ({
|
||||||
nodes,
|
nodes,
|
||||||
@@ -337,7 +351,7 @@ export const checkWorkflowNodeAndConnection = ({
|
|||||||
|
|
||||||
// check reference invalid
|
// check reference invalid
|
||||||
const renderType = input.renderTypeList[input.selectedTypeIndex || 0];
|
const renderType = input.renderTypeList[input.selectedTypeIndex || 0];
|
||||||
if (renderType === FlowNodeInputTypeEnum.reference && input.required) {
|
if (renderType === FlowNodeInputTypeEnum.reference) {
|
||||||
const checkReference = (value: [string, string]) => {
|
const checkReference = (value: [string, string]) => {
|
||||||
if (value[0] === VARIABLE_NODE_ID) {
|
if (value[0] === VARIABLE_NODE_ID) {
|
||||||
return false;
|
return false;
|
||||||
@@ -348,18 +362,16 @@ export const checkWorkflowNodeAndConnection = ({
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sourceOutput = sourceNode.data.outputs.find((item) => item.id === value[1]);
|
const sourceOutput = filterWorkflowNodeOutputsByType(
|
||||||
|
sourceNode.data.outputs,
|
||||||
|
input.valueType as WorkflowIOValueTypeEnum
|
||||||
|
).find((item) => item.id === value[1]);
|
||||||
return !sourceOutput;
|
return !sourceOutput;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Old format
|
// Old format
|
||||||
if (
|
if (isReferenceValueFormat(input.value)) {
|
||||||
Array.isArray(input.value) &&
|
return input.required && checkReference(input.value);
|
||||||
input.value.length === 2 &&
|
|
||||||
typeof input.value[0] === 'string' &&
|
|
||||||
typeof input.value[1] === 'string'
|
|
||||||
) {
|
|
||||||
return checkReference(input.value as [string, string]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New format
|
// New format
|
||||||
|
|||||||
Reference in New Issue
Block a user