mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 05:12:39 +00:00
feat: add workflow helperline (#2356)
* feat: add workflow helperline * del console * across zoom & adjust distance
This commit is contained in:
14
packages/global/core/workflow/type/index.d.ts
vendored
14
packages/global/core/workflow/type/index.d.ts
vendored
@@ -80,3 +80,17 @@ export type SystemPluginTemplateItemType = WorkflowTemplateType & {
|
|||||||
|
|
||||||
workflow: WorkflowTemplateBasicType;
|
workflow: WorkflowTemplateBasicType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type THelperLine = {
|
||||||
|
position: number;
|
||||||
|
nodes: {
|
||||||
|
left: number;
|
||||||
|
right: number;
|
||||||
|
top: number;
|
||||||
|
bottom: number;
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
centerX: number;
|
||||||
|
centerY: number;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
@@ -0,0 +1,97 @@
|
|||||||
|
import { THelperLine } from '@fastgpt/global/core/workflow/type';
|
||||||
|
import { CSSProperties, useEffect, useRef } from 'react';
|
||||||
|
import { ReactFlowState, useStore, useViewport } from 'reactflow';
|
||||||
|
|
||||||
|
const canvasStyle: CSSProperties = {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
position: 'absolute',
|
||||||
|
zIndex: 10,
|
||||||
|
pointerEvents: 'none'
|
||||||
|
};
|
||||||
|
|
||||||
|
const storeSelector = (state: ReactFlowState) => ({
|
||||||
|
width: state.width,
|
||||||
|
height: state.height,
|
||||||
|
transform: state.transform
|
||||||
|
});
|
||||||
|
|
||||||
|
export type HelperLinesProps = {
|
||||||
|
horizontal?: THelperLine;
|
||||||
|
vertical?: THelperLine;
|
||||||
|
};
|
||||||
|
|
||||||
|
function HelperLinesRenderer({ horizontal, vertical }: HelperLinesProps) {
|
||||||
|
const { width, height, transform } = useStore(storeSelector);
|
||||||
|
const { zoom } = useViewport();
|
||||||
|
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const ctx = canvas?.getContext('2d');
|
||||||
|
|
||||||
|
if (!ctx || !canvas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dpi = window.devicePixelRatio;
|
||||||
|
canvas.width = width * dpi;
|
||||||
|
canvas.height = height * dpi;
|
||||||
|
|
||||||
|
ctx.scale(dpi, dpi);
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
ctx.strokeStyle = '#D92D20';
|
||||||
|
|
||||||
|
const drawCross = (x: number, y: number, size: number) => {
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(x - size, y - size);
|
||||||
|
ctx.lineTo(x + size, y + size);
|
||||||
|
ctx.moveTo(x + size, y - size);
|
||||||
|
ctx.lineTo(x - size, y + size);
|
||||||
|
ctx.stroke();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (vertical) {
|
||||||
|
const x = vertical.position * transform[2] + transform[0];
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(
|
||||||
|
x,
|
||||||
|
Math.min(...vertical.nodes.map((node) => node.top)) * transform[2] + transform[1]
|
||||||
|
);
|
||||||
|
ctx.lineTo(
|
||||||
|
x,
|
||||||
|
Math.max(...vertical.nodes.map((node) => node.bottom)) * transform[2] + transform[1]
|
||||||
|
);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
vertical.nodes.forEach((node) => {
|
||||||
|
drawCross(x, node.top * transform[2] + transform[1], 5 * zoom);
|
||||||
|
drawCross(x, node.bottom * transform[2] + transform[1], 5 * zoom);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (horizontal) {
|
||||||
|
const y = horizontal.position * transform[2] + transform[1];
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(
|
||||||
|
Math.min(...horizontal.nodes.map((node) => node.left)) * transform[2] + transform[0],
|
||||||
|
y
|
||||||
|
);
|
||||||
|
ctx.lineTo(
|
||||||
|
Math.max(...horizontal.nodes.map((node) => node.right)) * transform[2] + transform[0],
|
||||||
|
y
|
||||||
|
);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
horizontal.nodes.forEach((node) => {
|
||||||
|
drawCross(node.left * transform[2] + transform[0], y, 5 * zoom);
|
||||||
|
drawCross(node.right * transform[2] + transform[0], y, 5 * zoom);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [width, height, transform, horizontal, vertical]);
|
||||||
|
|
||||||
|
return <canvas ref={canvasRef} style={canvasStyle} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default HelperLinesRenderer;
|
@@ -3,6 +3,14 @@ import { WorkflowContext } from '../../context';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
|
import { Node, NodePositionChange, XYPosition } from 'reactflow';
|
||||||
|
import { THelperLine } from '@fastgpt/global/core/workflow/type';
|
||||||
|
|
||||||
|
type GetHelperLinesResult = {
|
||||||
|
horizontal?: THelperLine;
|
||||||
|
vertical?: THelperLine;
|
||||||
|
snapPosition: Partial<XYPosition>;
|
||||||
|
};
|
||||||
|
|
||||||
export const useWorkflowUtils = () => {
|
export const useWorkflowUtils = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -32,8 +40,235 @@ export const useWorkflowUtils = () => {
|
|||||||
[nodeList]
|
[nodeList]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getHelperLines = (
|
||||||
|
change: NodePositionChange,
|
||||||
|
nodes: Node[],
|
||||||
|
distance = 8
|
||||||
|
): GetHelperLinesResult => {
|
||||||
|
const nodeA = nodes.find((node) => node.id === change.id);
|
||||||
|
|
||||||
|
if (!nodeA || !change.position) {
|
||||||
|
return {
|
||||||
|
horizontal: undefined,
|
||||||
|
vertical: undefined,
|
||||||
|
snapPosition: { x: undefined, y: undefined }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeABounds = {
|
||||||
|
left: change.position.x,
|
||||||
|
right: change.position.x + (nodeA.width ?? 0),
|
||||||
|
top: change.position.y,
|
||||||
|
bottom: change.position.y + (nodeA.height ?? 0),
|
||||||
|
width: nodeA.width ?? 0,
|
||||||
|
height: nodeA.height ?? 0,
|
||||||
|
centerX: change.position.x + (nodeA.width ?? 0) / 2,
|
||||||
|
centerY: change.position.y + (nodeA.height ?? 0) / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
let horizontalDistance = distance;
|
||||||
|
let verticalDistance = distance;
|
||||||
|
|
||||||
|
return nodes
|
||||||
|
.filter((node) => node.id !== nodeA.id)
|
||||||
|
.reduce<GetHelperLinesResult>(
|
||||||
|
(result, nodeB) => {
|
||||||
|
if (!result.vertical) {
|
||||||
|
result.vertical = {
|
||||||
|
position: nodeABounds.centerX,
|
||||||
|
nodes: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!result.horizontal) {
|
||||||
|
result.horizontal = {
|
||||||
|
position: nodeABounds.centerY,
|
||||||
|
nodes: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const nodeBBounds = {
|
||||||
|
left: nodeB.position.x,
|
||||||
|
right: nodeB.position.x + (nodeB.width ?? 0),
|
||||||
|
top: nodeB.position.y,
|
||||||
|
bottom: nodeB.position.y + (nodeB.height ?? 0),
|
||||||
|
width: nodeB.width ?? 0,
|
||||||
|
height: nodeB.height ?? 0,
|
||||||
|
centerX: nodeB.position.x + (nodeB.width ?? 0) / 2,
|
||||||
|
centerY: nodeB.position.y + (nodeB.height ?? 0) / 2
|
||||||
|
};
|
||||||
|
|
||||||
|
const distanceLeftLeft = Math.abs(nodeABounds.left - nodeBBounds.left);
|
||||||
|
const distanceRightRight = Math.abs(nodeABounds.right - nodeBBounds.right);
|
||||||
|
const distanceLeftRight = Math.abs(nodeABounds.left - nodeBBounds.right);
|
||||||
|
const distanceRightLeft = Math.abs(nodeABounds.right - nodeBBounds.left);
|
||||||
|
const distanceTopTop = Math.abs(nodeABounds.top - nodeBBounds.top);
|
||||||
|
const distanceBottomTop = Math.abs(nodeABounds.bottom - nodeBBounds.top);
|
||||||
|
const distanceBottomBottom = Math.abs(nodeABounds.bottom - nodeBBounds.bottom);
|
||||||
|
const distanceTopBottom = Math.abs(nodeABounds.top - nodeBBounds.bottom);
|
||||||
|
const distanceCenterXCenterX = Math.abs(nodeABounds.centerX - nodeBBounds.centerX);
|
||||||
|
const distanceCenterYCenterY = Math.abs(nodeABounds.centerY - nodeBBounds.centerY);
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceLeftLeft < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.left;
|
||||||
|
result.vertical.position = nodeBBounds.left;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceLeftLeft;
|
||||||
|
} else if (distanceLeftLeft === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceRightRight < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.right - nodeABounds.width;
|
||||||
|
result.vertical.position = nodeBBounds.right;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceRightRight;
|
||||||
|
} else if (distanceRightRight === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceLeftRight < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.right;
|
||||||
|
result.vertical.position = nodeBBounds.right;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceLeftRight;
|
||||||
|
} else if (distanceLeftRight === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceRightLeft < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.left - nodeABounds.width;
|
||||||
|
result.vertical.position = nodeBBounds.left;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceRightLeft;
|
||||||
|
} else if (distanceRightLeft === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A | | B |
|
||||||
|
// |___________| |___________|
|
||||||
|
if (distanceTopTop < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.top;
|
||||||
|
result.horizontal.position = nodeBBounds.top;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceTopTop;
|
||||||
|
} else if (distanceTopTop === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|_________________
|
||||||
|
// | |
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceBottomTop < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.top - nodeABounds.height;
|
||||||
|
result.horizontal.position = nodeBBounds.top;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceBottomTop;
|
||||||
|
} else if (distanceBottomTop === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾| |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A | | B |
|
||||||
|
// |___________|_____|___________|
|
||||||
|
if (distanceBottomBottom < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.bottom - nodeABounds.height;
|
||||||
|
result.horizontal.position = nodeBBounds.bottom;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceBottomBottom;
|
||||||
|
} else if (distanceBottomBottom === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// | |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
if (distanceTopBottom < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.bottom;
|
||||||
|
result.horizontal.position = nodeBBounds.bottom;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceTopBottom;
|
||||||
|
} else if (distanceTopBottom === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |
|
||||||
|
// |___________|
|
||||||
|
// |
|
||||||
|
// |
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | B |
|
||||||
|
// |___________|
|
||||||
|
if (distanceCenterXCenterX < verticalDistance) {
|
||||||
|
result.snapPosition.x = nodeBBounds.centerX - nodeABounds.width / 2;
|
||||||
|
result.vertical.position = nodeBBounds.centerX;
|
||||||
|
result.vertical.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
verticalDistance = distanceCenterXCenterX;
|
||||||
|
} else if (distanceCenterXCenterX === verticalDistance) {
|
||||||
|
result.vertical.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
// |‾‾‾‾‾‾‾‾‾‾‾| |‾‾‾‾‾‾‾‾‾‾‾|
|
||||||
|
// | A |----| B |
|
||||||
|
// |___________| |___________|
|
||||||
|
if (distanceCenterYCenterY < horizontalDistance) {
|
||||||
|
result.snapPosition.y = nodeBBounds.centerY - nodeABounds.height / 2;
|
||||||
|
result.horizontal.position = nodeBBounds.centerY;
|
||||||
|
result.horizontal.nodes = [nodeABounds, nodeBBounds];
|
||||||
|
horizontalDistance = distanceCenterYCenterY;
|
||||||
|
} else if (distanceCenterYCenterY === horizontalDistance) {
|
||||||
|
result.horizontal.nodes.push(nodeBBounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
{ snapPosition: { x: undefined, y: undefined } } as GetHelperLinesResult
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
computedNewNodeName
|
computedNewNodeName,
|
||||||
|
getHelperLines
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -1,5 +1,14 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { Connection, NodeChange, OnConnectStartParams, addEdge, EdgeChange, Edge } from 'reactflow';
|
import {
|
||||||
|
Connection,
|
||||||
|
NodeChange,
|
||||||
|
OnConnectStartParams,
|
||||||
|
addEdge,
|
||||||
|
EdgeChange,
|
||||||
|
Edge,
|
||||||
|
applyNodeChanges,
|
||||||
|
Node
|
||||||
|
} from 'reactflow';
|
||||||
import { EDGE_TYPE } from '@fastgpt/global/core/workflow/node/constant';
|
import { EDGE_TYPE } from '@fastgpt/global/core/workflow/node/constant';
|
||||||
import 'reactflow/dist/style.css';
|
import 'reactflow/dist/style.css';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
@@ -8,6 +17,7 @@ import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
|||||||
import { useKeyboard } from './useKeyboard';
|
import { useKeyboard } from './useKeyboard';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { WorkflowContext } from '../../context';
|
import { WorkflowContext } from '../../context';
|
||||||
|
import { useWorkflowUtils } from './useUtils';
|
||||||
|
|
||||||
export const useWorkflow = () => {
|
export const useWorkflow = () => {
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@@ -18,12 +28,47 @@ export const useWorkflow = () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const { isDowningCtrl } = useKeyboard();
|
const { isDowningCtrl } = useKeyboard();
|
||||||
const { setConnectingEdge, nodes, onNodesChange, setEdges, onEdgesChange, setHoverEdgeId } =
|
const {
|
||||||
useContextSelector(WorkflowContext, (v) => v);
|
setConnectingEdge,
|
||||||
|
nodes,
|
||||||
|
setNodes,
|
||||||
|
onNodesChange,
|
||||||
|
setEdges,
|
||||||
|
onEdgesChange,
|
||||||
|
setHoverEdgeId,
|
||||||
|
setHelperLineHorizontal,
|
||||||
|
setHelperLineVertical
|
||||||
|
} = useContextSelector(WorkflowContext, (v) => v);
|
||||||
|
|
||||||
|
const { getHelperLines } = useWorkflowUtils();
|
||||||
|
|
||||||
|
const customApplyNodeChanges = useCallback((changes: NodeChange[], nodes: Node[]): Node[] => {
|
||||||
|
setHelperLineHorizontal(undefined);
|
||||||
|
setHelperLineVertical(undefined);
|
||||||
|
|
||||||
|
if (
|
||||||
|
changes.length === 1 &&
|
||||||
|
changes[0].type === 'position' &&
|
||||||
|
changes[0].dragging &&
|
||||||
|
changes[0].position
|
||||||
|
) {
|
||||||
|
const helperLines = getHelperLines(changes[0], nodes);
|
||||||
|
|
||||||
|
changes[0].position.x = helperLines.snapPosition.x ?? changes[0].position.x;
|
||||||
|
changes[0].position.y = helperLines.snapPosition.y ?? changes[0].position.y;
|
||||||
|
|
||||||
|
setHelperLineHorizontal(helperLines.horizontal);
|
||||||
|
setHelperLineVertical(helperLines.vertical);
|
||||||
|
}
|
||||||
|
|
||||||
|
return applyNodeChanges(changes, nodes);
|
||||||
|
}, []);
|
||||||
|
|
||||||
/* node */
|
/* node */
|
||||||
const handleNodesChange = useCallback(
|
const handleNodesChange = useCallback(
|
||||||
(changes: NodeChange[]) => {
|
(changes: NodeChange[]) => {
|
||||||
|
setNodes((nodes) => customApplyNodeChanges(changes, nodes));
|
||||||
|
|
||||||
for (const change of changes) {
|
for (const change of changes) {
|
||||||
if (change.type === 'remove') {
|
if (change.type === 'remove') {
|
||||||
const node = nodes.find((n) => n.id === change.id);
|
const node = nodes.find((n) => n.id === change.id);
|
||||||
|
@@ -26,6 +26,7 @@ import { useContextSelector } from 'use-context-selector';
|
|||||||
import { WorkflowContext } from '../context';
|
import { WorkflowContext } from '../context';
|
||||||
import { useWorkflow } from './hooks/useWorkflow';
|
import { useWorkflow } from './hooks/useWorkflow';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
|
import HelperLines from './components/HelperLines';
|
||||||
|
|
||||||
const NodeSimple = dynamic(() => import('./nodes/NodeSimple'));
|
const NodeSimple = dynamic(() => import('./nodes/NodeSimple'));
|
||||||
const nodeTypes: Record<FlowNodeTypeEnum, any> = {
|
const nodeTypes: Record<FlowNodeTypeEnum, any> = {
|
||||||
@@ -62,7 +63,8 @@ const edgeTypes = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const Workflow = () => {
|
const Workflow = () => {
|
||||||
const { nodes, edges, reactFlowWrapper } = useContextSelector(WorkflowContext, (v) => v);
|
const { nodes, edges, reactFlowWrapper, helperLineHorizontal, helperLineVertical } =
|
||||||
|
useContextSelector(WorkflowContext, (v) => v);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
ConfirmDeleteModal,
|
ConfirmDeleteModal,
|
||||||
@@ -135,6 +137,7 @@ const Workflow = () => {
|
|||||||
onEdgeMouseLeave={onEdgeMouseLeave}
|
onEdgeMouseLeave={onEdgeMouseLeave}
|
||||||
>
|
>
|
||||||
<FlowController />
|
<FlowController />
|
||||||
|
<HelperLines horizontal={helperLineHorizontal} vertical={helperLineVertical} />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
@@ -48,6 +48,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|||||||
import { formatTime2HM, formatTime2YMDHMW } from '@fastgpt/global/common/string/time';
|
import { formatTime2HM, formatTime2YMDHMW } from '@fastgpt/global/common/string/time';
|
||||||
import type { InitProps } from '@/pages/app/detail/components/PublishHistoriesSlider';
|
import type { InitProps } from '@/pages/app/detail/components/PublishHistoriesSlider';
|
||||||
import { cloneDeep } from 'lodash';
|
import { cloneDeep } from 'lodash';
|
||||||
|
import { THelperLine } from '@fastgpt/global/core/workflow/type';
|
||||||
|
|
||||||
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
type OnChange<ChangesType> = (changes: ChangesType[]) => void;
|
||||||
|
|
||||||
@@ -135,6 +136,12 @@ type WorkflowContextType = {
|
|||||||
historiesDefaultData?: InitProps;
|
historiesDefaultData?: InitProps;
|
||||||
setHistoriesDefaultData: React.Dispatch<React.SetStateAction<undefined | InitProps>>;
|
setHistoriesDefaultData: React.Dispatch<React.SetStateAction<undefined | InitProps>>;
|
||||||
|
|
||||||
|
// helper line
|
||||||
|
helperLineHorizontal?: THelperLine;
|
||||||
|
setHelperLineHorizontal: React.Dispatch<React.SetStateAction<THelperLine | undefined>>;
|
||||||
|
helperLineVertical?: THelperLine;
|
||||||
|
setHelperLineVertical: React.Dispatch<React.SetStateAction<THelperLine | undefined>>;
|
||||||
|
|
||||||
// chat test
|
// chat test
|
||||||
setWorkflowTestData: React.Dispatch<
|
setWorkflowTestData: React.Dispatch<
|
||||||
React.SetStateAction<
|
React.SetStateAction<
|
||||||
@@ -260,6 +267,14 @@ export const WorkflowContext = createContext<WorkflowContextType>({
|
|||||||
setHistoriesDefaultData: function (value: React.SetStateAction<InitProps | undefined>): void {
|
setHistoriesDefaultData: function (value: React.SetStateAction<InitProps | undefined>): void {
|
||||||
throw new Error('Function not implemented.');
|
throw new Error('Function not implemented.');
|
||||||
},
|
},
|
||||||
|
helperLineHorizontal: undefined,
|
||||||
|
setHelperLineHorizontal: function (value: React.SetStateAction<THelperLine | undefined>): void {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
|
helperLineVertical: undefined,
|
||||||
|
setHelperLineVertical: function (value: React.SetStateAction<THelperLine | undefined>): void {
|
||||||
|
throw new Error('Function not implemented.');
|
||||||
|
},
|
||||||
getNodeDynamicInputs: function (nodeId: string): FlowNodeInputItemType[] {
|
getNodeDynamicInputs: function (nodeId: string): FlowNodeInputItemType[] {
|
||||||
throw new Error('Function not implemented.');
|
throw new Error('Function not implemented.');
|
||||||
}
|
}
|
||||||
@@ -728,6 +743,11 @@ const WorkflowContextProvider = ({
|
|||||||
/* Version histories */
|
/* Version histories */
|
||||||
const [historiesDefaultData, setHistoriesDefaultData] = useState<InitProps>();
|
const [historiesDefaultData, setHistoriesDefaultData] = useState<InitProps>();
|
||||||
|
|
||||||
|
/* helper line */
|
||||||
|
const [helperLineHorizontal, setHelperLineHorizontal] = useState<THelperLine | undefined>(
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
const [helperLineVertical, setHelperLineVertical] = useState<THelperLine | undefined>(undefined);
|
||||||
/* event bus */
|
/* event bus */
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eventBus.on(EventNameEnum.requestWorkflowStore, () => {
|
eventBus.on(EventNameEnum.requestWorkflowStore, () => {
|
||||||
@@ -797,6 +817,12 @@ const WorkflowContextProvider = ({
|
|||||||
historiesDefaultData,
|
historiesDefaultData,
|
||||||
setHistoriesDefaultData,
|
setHistoriesDefaultData,
|
||||||
|
|
||||||
|
// helper line
|
||||||
|
helperLineHorizontal,
|
||||||
|
setHelperLineHorizontal,
|
||||||
|
helperLineVertical,
|
||||||
|
setHelperLineVertical,
|
||||||
|
|
||||||
// chat test
|
// chat test
|
||||||
setWorkflowTestData
|
setWorkflowTestData
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user