Merge pull request #119 from SuanmoSuanyangTechnology/fix/workflow_zy

Fix/workflow zy
This commit is contained in:
yingzhao
2026-01-15 13:43:03 +08:00
committed by GitHub
18 changed files with 211 additions and 72 deletions

View File

@@ -30,15 +30,12 @@ import 'dayjs/plugin/utc'
import { cookieUtils } from './utils/request';
function App() {
const { t } = useTranslation();
const { locale, language, timeZone } = useI18n()
useEffect(() => {
const authToken = cookieUtils.get('authToken')
if (!authToken && !window.location.hash.includes('#/login')) {
if (!authToken && !window.location.hash.includes('#/login') && !window.location.hash.includes('#/conversation/')) {
window.location.href = `/#/login`;
}
}, [])

View File

@@ -0,0 +1,14 @@
import { Outlet } from 'react-router-dom';
import { type FC } from 'react';
// 基础布局组件,用于展示内容并保留用户信息获取功能
const NoAuthLayout: FC = () => {
return (
<div className="rb:relative rb:h-full rb:w-full">
<Outlet />
</div>
)
};
export default NoAuthLayout;

View File

@@ -81,7 +81,7 @@ const Code: FC<ICodeProps> = (props) => {
</div>
)
}
return <span className="rb:bg-[#F0F3F8] rb:px-1 rb:py-0.5 rb:rounded rb:text-sm rb:font-mono">{children}</span>
return <code className="rb:bg-[#F0F3F8] rb:px-1 rb:py-0.5 rb:rounded rb:text-sm rb:font-mono rb:whitespace-break-spaces">{children}</code>
}
export default Code

View File

@@ -1960,6 +1960,9 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re
addMessage: 'Add Message',
answerDesc: 'Reply',
addNode: 'Add Node',
arrange: 'Arrange',
redo: 'Redo',
undo: 'Undo',
},
emotionEngine: {
emotionEngineConfig: 'Emotion Engine Configuration',

View File

@@ -2057,6 +2057,9 @@ export const zh = {
addMessage: '添加消息',
answerDesc: '回复',
addNode: '添加节点',
arrange: '整理',
redo: '重做',
undo: '撤销',
},
emotionEngine: {
emotionEngineConfig: '情感引擎配置',

View File

@@ -34,6 +34,7 @@ const componentMap: Record<string, LazyExoticComponent<ComponentType<object>>> =
AuthSpaceLayout: lazy(() => import('@/components/Layout/AuthSpaceLayout')),
BasicLayout: lazy(() => import('@/components/Layout/BasicLayout')),
LoginLayout: lazy(() => import('@/components/Layout/LoginLayout')),
NoAuthLayout: lazy(() => import('@/components/Layout/NoAuthLayout')),
// 视图组件
Index: lazy(() => import('@/views/Index')),
Home: lazy(() => import('@/views/Home')),

View File

@@ -42,12 +42,17 @@
"element": "BasicLayout",
"children": [
{ "path": "/application/config/:id", "element": "ApplicationConfig" },
{ "path": "/conversation/:token", "element": "Conversation" },
{ "path": "/user-memory/neo4j/:id", "element": "Neo4jUserMemoryDetail" },
{ "path": "/statement/:id", "element": "StatementDetail" },
{ "path": "/user-memory/detail/:id/:type", "element": "MemoryNodeDetail" }
]
},
{
"element": "NoAuthLayout",
"children": [
{ "path": "/conversation/:token", "element": "Conversation" }
]
},
{
"element": "LoginLayout",
"children": [

View File

@@ -41,7 +41,9 @@ export default function UserMemory() {
navigate(`/user-memory/${id}`)
}
}
const handleViewMemoryConfig = () => {
const handleViewMemoryConfig = (e: React.MouseEvent) => {
e.preventDefault();
e.stopPropagation();
navigate(`/memory`)
}
@@ -84,8 +86,9 @@ export default function UserMemory() {
title={name || '-'}
extra={<div
className="rb:w-7 rb:h-7 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/userMemory/goto.svg')]"
onClick={() => handleViewDetail(end_user.id)}
></div>}
className="rb:cursor-pointer"
onClick={() => handleViewDetail(end_user.id)}
>
<div className="rb:flex rb:justify-between rb:items-center">
<div>{t('userMemory.capacity')}</div>
@@ -96,7 +99,7 @@ export default function UserMemory() {
<div>{t(`userMemory.${item.type || 'person'}`)}</div>
</div>
<div className="rb:mt-3 rb:bg-[#F6F8FC] rb:rounded-lg rb:border rb:border-[#DFE4ED] rb:py-2 rb:px-3" onClick={handleViewMemoryConfig}>
<div className="rb:relative rb:z-2 rb:mt-3 rb:bg-[#F6F8FC] rb:rounded-lg rb:border rb:border-[#DFE4ED] rb:py-2 rb:px-3" onClick={handleViewMemoryConfig}>
<div className="rb:text-[#5B6167] rb:leading-5 rb:flex rb:justify-between rb:items-center">
{t('userMemory.memory_config_name')}
<div

View File

@@ -81,12 +81,14 @@ const RelationshipNetwork:FC = () => {
name: displayName,
category: categoryIndex >= 0 ? categoryIndex : 0,
symbolSize: symbolSize, // 根据连接数调整节点大小
itemStyle: {
color: colors[categoryIndex % 8]
}
})
})
// 创建节点ID到标签的映射
const nodeIdToLabel: Record<string, string> = {}
nodes.forEach(node => {
nodeIdToLabel[node.id] = node.label
})
// 处理边数据
edges.forEach(edge => {
curEdges.push({

View File

@@ -40,7 +40,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef }>(({ appId
const curVariables = startNodes[0].config.variables?.defaultValue
curVariables.forEach((vo: StartVariableItem) => {
if (vo.default) {
if (typeof vo.default !== 'undefined') {
vo.value = vo.default
}
const lastVo = variables.find(item => item.name === vo.name)
@@ -55,6 +55,7 @@ const Chat = forwardRef<ChatRef, { appId: string; graphRef: GraphRef }>(({ appId
setOpen(false)
setChatList([])
setVariables([])
setConversationId(null)
}
const handleEditVariables = () => {
variableConfigModalRef.current?.handleOpen(variables)

View File

@@ -13,11 +13,12 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
const handleNodeSelect = (selectedNodeType: any) => {
const parentBBox = node.getBBox();
const cycleId = data.cycle;
const horizontalSpacing = 20;
const id = `${selectedNodeType.type.replace(/-/g, '_') }_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
const newNode = graph.addNode({
...(graphNodeLibrary[selectedNodeType.type] || graphNodeLibrary.default),
x: parentBBox.x,
x: parentBBox.x + horizontalSpacing,
y: parentBBox.y,
id,
data: {
@@ -47,7 +48,6 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
source: { cell: edge.getSourceCellId(), port: edge.getSourcePortId() },
target: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'left')?.id || 'left' },
attrs: edge.getAttrs(),
zIndex: 3
});
});
@@ -58,7 +58,6 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
source: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'right')?.id || 'right' },
target: { cell: edge.getTargetCellId(), port: targetPortId },
attrs: edge.getAttrs(),
zIndex: 3
});
});

View File

@@ -11,10 +11,14 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
const { t } = useTranslation()
useEffect(() => {
initNodes()
// 检查是否需要添加add-node
checkAndAddAddNode()
}, [])
// 使用setTimeout确保在所有节点都添加完成后再创建连线
const timer = setTimeout(() => {
initNodes()
checkAndAddAddNode()
}, 50)
return () => clearTimeout(timer)
}, [graph])
const checkAndAddAddNode = () => {
if (!graph) return;
@@ -29,7 +33,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
const addNode = graph.addNode({
...graphNodeLibrary.addStart,
x: cycleStartBBox.x + 64,
x: cycleStartBBox.x + 84,
y: cycleStartBBox.y,
data: {
type: 'add-node',
@@ -47,7 +51,8 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
const targetPorts = addNode.getPorts();
const sourcePort = sourcePorts.find((port: any) => port.group === 'right')?.id || 'right';
const targetPort = targetPorts.find((port: any) => port.group === 'left')?.id || 'left';
// 然后创建连线
graph.addEdge({
source: { cell: cycleStartNode.id, port: sourcePort },
target: { cell: addNode.id, port: targetPort },
@@ -61,7 +66,6 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
},
},
},
zIndex: 10
});
}
}
@@ -93,7 +97,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
});
const addNode = graph.addNode({
...graphNodeLibrary.addStart,
x: centerX + 64,
x: centerX + 84,
y: centerY,
data: {
type: 'add-node',
@@ -124,13 +128,11 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
strokeWidth: 1,
targetMarker: {
name: 'block',
size: 8,
size: 2,
},
},
},
zIndex: 10
}
graph.addEdge(edgeConfig)
}

View File

@@ -36,11 +36,77 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
if (!sourceNode || !graph) return;
const sourceNodeData = sourceNode.getData();
const sourceNodeType = sourceNodeData?.type;
// 计算新节点位置(在源节点右侧)
// 如果是cycle-start节点需要处理add-node节点
let addNodePosition = null;
if (sourceNodeType === 'cycle-start' && sourceNodeData.cycle) {
const cycleId = sourceNodeData.cycle;
const addNodes = graph.getNodes().filter((n: any) =>
n.getData()?.type === 'add-node' && n.getData()?.cycle === cycleId
);
if (addNodes.length > 0) {
const addNode = addNodes[0];
addNodePosition = addNode.getBBox();
addNode.remove();
}
}
// 计算新节点位置,避免重叠
const sourceBBox = sourceNode.getBBox();
const newX = sourceBBox.x + sourceBBox.width + 50;
const newY = sourceBBox.y;
const nodeWidth = graphNodeLibrary[selectedNodeType.type]?.width || 120;
const nodeHeight = graphNodeLibrary[selectedNodeType.type]?.height || 88;
const horizontalSpacing = sourceNodeType === 'cycle-start' ? 40 : 80;
const verticalSpacing = 10;
// 获取源连接桩的group信息
const sourcePortInfo = sourceNode.getPorts().find((p: any) => p.id === sourcePort);
const sourcePortGroup = sourcePortInfo?.group || sourcePort;
console.log('sourcePortGroup', sourcePortGroup, sourcePortInfo)
// 如果有add-node位置使用该位置否则计算新位置
let newX, newY;
if (addNodePosition) {
newX = addNodePosition.x;
newY = addNodePosition.y;
} else {
// 根据连接桩位置决定节点放置方向
if (sourcePortGroup === 'left') {
// 左侧连接桩,在左侧添加节点
newX = sourceBBox.x - nodeWidth*2 - horizontalSpacing;
newY = sourceBBox.y;
} else {
// 右侧连接桩,在右侧添加节点
newX = sourceBBox.x + sourceBBox.width + horizontalSpacing;
newY = sourceBBox.y;
}
// 检查位置是否与现有节点重叠(只考虑与当前节点相连的节点)
const checkOverlap = (x: number, y: number) => {
// 获取与源节点相连的节点
const connectedNodes = new Set();
graph.getConnectedEdges(sourceNode).forEach((edge: any) => {
const sourceId = edge.getSourceCellId();
const targetId = edge.getTargetCellId();
if (sourceId !== sourceNode.id) connectedNodes.add(sourceId);
if (targetId !== sourceNode.id) connectedNodes.add(targetId);
});
return graph.getNodes().some((node: any) => {
if (node.id === sourceNode.id) return false;
if (!connectedNodes.has(node.id)) return false; // 只考虑相连的节点
const bbox = node.getBBox();
return !(x + nodeWidth < bbox.x || x > bbox.x + bbox.width ||
y + nodeHeight < bbox.y || y > bbox.y + bbox.height);
});
};
// 如果位置被占用,向下寻找空位
while (checkOverlap(newX, newY)) {
newY += nodeHeight + verticalSpacing;
}
}
// 创建新节点
const id = `${selectedNodeType.type.replace(/-/g, '_')}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
@@ -70,7 +136,15 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
// 创建连线
setTimeout(() => {
const targetPorts = newNode.getPorts();
const targetPort = targetPorts.find((port: any) => port.group === 'left')?.id || 'left';
let targetPort;
if (sourcePortGroup === 'left') {
// 从左侧连接桩连出,连接到新节点的右侧
targetPort = targetPorts.find((port: any) => port.group === 'right')?.id || 'right';
} else {
// 从右侧连接桩连出,连接到新节点的左侧
targetPort = targetPorts.find((port: any) => port.group === 'left')?.id || 'left';
}
graph.addEdge({
source: { cell: sourceNode.id, port: sourcePort },
@@ -85,7 +159,7 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
},
},
},
zIndex: 0
// zIndex: sourceNodeData.cycle && sourceNodeType == 'cycle-start' ? 1 : sourceNodeData.cycle ? 2 : 0
});
// 循环节点内子节点通过连接桩添加时,调整循环节点大小
@@ -108,8 +182,9 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
}, { minX: Infinity, minY: Infinity, maxX: -Infinity, maxY: -Infinity });
const padding = 20;
const bottomPadding = 50;
const newWidth = Math.max(240, bounds.maxX - bounds.minX + padding * 2);
const newHeight = Math.max(120, bounds.maxY - bounds.minY + padding * 2);
const newHeight = Math.max(120, bounds.maxY - bounds.minY + padding + bottomPadding);
parentNode.prop('size', { width: newWidth, height: newHeight });
}

View File

@@ -55,6 +55,11 @@ const CaseList: FC<CaseListProps> = ({
const updateNodePorts = (caseCount: number, removedCaseIndex?: number) => {
if (!selectedNode || !graphRef?.current) return;
// 获取当前端口数量来判断是添加还是删除操作
const currentPorts = selectedNode.getPorts().filter((port: any) => port.group === 'right');
const currentCaseCount = currentPorts.length - 1; // 减去ELSE端口
const isAddingCase = removedCaseIndex === undefined && caseCount > currentCaseCount;
// 保存现有连线信息(包括左侧端口连线)
const existingEdges = graphRef.current.getEdges().filter((edge: any) =>
edge.getSourceCellId() === selectedNode.id || edge.getTargetCellId() === selectedNode.id
@@ -83,14 +88,10 @@ const CaseList: FC<CaseListProps> = ({
selectedNode.prop('size', { width: 240, height: newHeight })
// 计算端口间距
const dy = totalPorts;
// 添加 IF 端口
selectedNode.addPort({
id: 'CASE1',
group: 'right',
// args: { dy },
attrs: { text: { text: 'IF', fontSize: 12, fill: '#5B6167' }}
});
@@ -99,7 +100,6 @@ const CaseList: FC<CaseListProps> = ({
selectedNode.addPort({
id: `CASE${i + 1}`,
group: 'right',
// args: { dy },
attrs: { text: { text: 'ELIF', fontSize: 12, fill: '#5B6167' }}
});
}
@@ -108,22 +108,11 @@ const CaseList: FC<CaseListProps> = ({
selectedNode.addPort({
id: `CASE${caseCount + 1}`,
group: 'right',
// args: { dy },
attrs: { text: { text: 'ELSE', fontSize: 12, fill: '#5B6167' }}
});
// 恢复仍然存在的端口连线
// 恢复连线
setTimeout(() => {
// 计算删除前的总端口数来确定原ELSE端口编号
const originalCaseCount = removedCaseIndex !== undefined ? caseCount + 1 : caseCount;
const originalElsePortNumber = originalCaseCount + 1;
// 检查ELSE端口是否有连线
const elseHasConnection = edgeConnections.some(({ sourcePortId, isIncoming }: any) => {
const caseNumber = parseInt(sourcePortId.match(/CASE(\d+)/)?.[1] || '0');
return !isIncoming && caseNumber === originalElsePortNumber;
});
edgeConnections.forEach(({ edge, sourcePortId, targetCellId, targetPortId, sourceCellId, isIncoming }: any) => {
// 如果是进入连线(左侧端口),直接恢复
if (isIncoming) {
@@ -151,7 +140,7 @@ const CaseList: FC<CaseListProps> = ({
// 处理右侧端口连线
const originalCaseNumber = parseInt(sourcePortId.match(/CASE(\d+)/)?.[1] || '0');
// 如果是被删除的端口,删除该端口的连线
// 如果是删除操作且是被删除的端口,删除连线
if (removedCaseIndex !== undefined && originalCaseNumber === removedCaseIndex + 1) {
graphRef.current?.removeCell(edge);
return;
@@ -159,12 +148,22 @@ const CaseList: FC<CaseListProps> = ({
let newPortId = sourcePortId;
// 如果是原来的ELSE端口且有连线重新映射到新的ELSE端口
if (originalCaseNumber === originalElsePortNumber && elseHasConnection) {
newPortId = `CASE${caseCount + 1}`; // 新的ELSE端口
} else if (removedCaseIndex !== undefined && originalCaseNumber > removedCaseIndex + 1) {
// 如果是被删除端口之后的端口,编号向前移动
newPortId = `CASE${originalCaseNumber - 1}`;
// 如果是删除操作需要重新映射端口ID
if (removedCaseIndex !== undefined) {
if (originalCaseNumber > removedCaseIndex + 1) {
// 被删除端口之后的端口,编号向前移动
newPortId = `CASE${originalCaseNumber - 1}`;
}
// ELSE端口始终映射到新的ELSE端口位置
else if (originalCaseNumber === currentCaseCount + 1) {
newPortId = `CASE${caseCount + 1}`;
}
} else if (isAddingCase) {
// 如果是添加操作ELSE端口需要重新映射
if (originalCaseNumber === currentCaseCount + 1) {
newPortId = `CASE${caseCount + 1}`; // 新的ELSE端口
}
// 新添加的端口不恢复任何连线
}
const newPorts = selectedNode.getPorts();

View File

@@ -110,6 +110,7 @@ const VariableEditModal = forwardRef<VariableEditModalRef, VariableEditModalProp
value: key,
label: t(`workflow.config.start.${key}`),
}))}
onChange={() => form.setFieldValue('default', undefined)}
labelRender={(props) => <div className="rb:flex rb:justify-between rb:items-center">{props.label} <Tag color="blue">{variableType[props.value as keyof typeof variableType]}</Tag></div>}
optionRender={(props) => <div className="rb:flex rb:justify-between rb:items-center">{props.label} <Tag color="blue">{variableType[props.value as keyof typeof variableType]}</Tag></div>}
/>

View File

@@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next'
import { Graph, Node } from '@antv/x6';
import { Form, Input, Button, Select, InputNumber, Slider, Space, Divider, App, Switch } from 'antd'
import type { NodeConfig, NodeProperties, StartVariableItem, VariableEditModalRef } from '../../types'
import type { NodeConfig, NodeProperties, StartVariableItem, VariableEditModalRef, ChatVariable } from '../../types'
import Empty from '@/components/Empty';
import emptyIcon from '@/assets/images/workflow/empty.png'
import CustomSelect from "@/components/CustomSelect";
@@ -34,11 +34,12 @@ interface PropertiesProps {
copyEvent: () => void;
parseEvent: () => void;
config?: any;
chatVariables: ChatVariable[];
}
const Properties: FC<PropertiesProps> = ({
selectedNode,
graphRef,
config: workflowConfig,
chatVariables
}) => {
const { t } = useTranslation()
const { modal } = App.useApp()
@@ -47,6 +48,7 @@ const Properties: FC<PropertiesProps> = ({
const values = Form.useWatch([], form);
const variableModalRef = useRef<VariableEditModalRef>(null)
const [editIndex, setEditIndex] = useState<number | null>(null)
const [graphUpdateTrigger, setGraphUpdateTrigger] = useState(0)
const prevMappingNamesRef = useRef<string[]>([])
const prevTemplateVarsRef = useRef<string[]>([])
const syncTimeoutRef = useRef<number | null>(null)
@@ -242,6 +244,7 @@ const Properties: FC<PropertiesProps> = ({
}, [values, selectedNode, form])
const handleAddVariable = () => {
setEditIndex(null)
variableModalRef.current?.handleOpen()
}
const handleEditVariable = (index: number, vo: StartVariableItem) => {
@@ -250,6 +253,7 @@ const Properties: FC<PropertiesProps> = ({
}
const handleRefreshVariable = (value: StartVariableItem) => {
if (!selectedNode) return
if (editIndex !== null) {
const defaultValue = selectedNode.data.config.variables.defaultValue ?? []
defaultValue[editIndex] = value
@@ -260,7 +264,7 @@ const Properties: FC<PropertiesProps> = ({
}
selectedNode?.setData({ ...selectedNode.data})
setConfigs({ ...selectedNode.data.config})
setConfigs({ ...selectedNode.data.config })
}
const handleDeleteVariable = (index: number, vo: StartVariableItem) => {
if (!selectedNode) return
@@ -347,11 +351,9 @@ const Properties: FC<PropertiesProps> = ({
const parentPreviousNodeIds = getAllPreviousNodes(parentLoopNode.id);
allRelevantNodeIds.push(...parentPreviousNodeIds);
}
// Add conversation variables from global config
const conversationVariables = workflowConfig?.variables || [];
const conversationVariables = chatVariables || [];
conversationVariables.forEach((variable: any) => {
const key = `CONVERSATION_${variable.name}`;
@@ -761,7 +763,36 @@ const Properties: FC<PropertiesProps> = ({
}
return variableList;
}, [selectedNode, graphRef, workflowConfig?.variables]);
}, [selectedNode, graphRef, graphUpdateTrigger, chatVariables]);
// Trigger variableList update when graph edges or nodes change
useEffect(() => {
if (!graphRef?.current) return;
const graph = graphRef.current;
const handleGraphChange = () => {
console.log('handleGraphChange')
// Force variableList recalculation by updating trigger
setGraphUpdateTrigger(prev => prev + 1);
};
// Listen to graph changes
graph.on('edge:added', handleGraphChange);
graph.on('edge:removed', handleGraphChange);
graph.on('edge:changed', handleGraphChange);
graph.on('node:added', handleGraphChange);
graph.on('node:removed', handleGraphChange);
graph.on('node:change:data', handleGraphChange);
return () => {
graph.off('edge:added', handleGraphChange);
graph.off('edge:removed', handleGraphChange);
graph.off('edge:changed', handleGraphChange);
graph.off('node:added', handleGraphChange);
graph.off('node:removed', handleGraphChange);
graph.off('node:change:data', handleGraphChange);
};
}, [graphRef]);
// Filter out boolean type variables for loop and llm nodes
const getFilteredVariableList = (nodeType?: string, key?: string) => {

View File

@@ -54,7 +54,7 @@ export const useWorkflowGraph = ({
const historyRef = useRef<{ undoStack: string[], redoStack: string[] }>({ undoStack: [], redoStack: [] });
const [canUndo, setCanUndo] = useState(false);
const [canRedo, setCanRedo] = useState(false);
const [isHandMode, setIsHandMode] = useState(false);
const [isHandMode, setIsHandMode] = useState(true);
const [config, setConfig] = useState<WorkflowConfig | null>(null);
const [chatVariables, setChatVariables] = useState<ChatVariable[]>([])
@@ -275,6 +275,11 @@ export const useWorkflowGraph = ({
}, 100)
}
if (edges.length) {
// 计算loop和iteration类型节点的数量
const loopIterationCount = nodes.filter(node =>
node.type === 'loop' || node.type === 'iteration'
).length;
// 去重处理对于if-else和question-classifier节点不同连接桩允许连接到相同节点
const uniqueEdges = edges.filter((edge, index, arr) => {
return arr.findIndex(e => {
@@ -349,7 +354,7 @@ export const useWorkflowGraph = ({
},
},
},
zIndex: targetCell.getData()?.cycle ? 3 : 0
// zIndex: loopIterationCount
}
return edgeConfig
@@ -689,10 +694,9 @@ export const useWorkflowGraph = ({
thickness: 1, // 网点大小
}
},
panning: false,
panning: isHandMode,
mousewheel: {
enabled: true,
modifiers: ['ctrl', 'meta'],
},
connecting: {
// router: 'orth',
@@ -725,7 +729,6 @@ export const useWorkflowGraph = ({
},
},
},
zIndex: 0,
});
},
validateConnection({ sourceCell, targetCell, targetMagnet }) {
@@ -762,9 +765,8 @@ export const useWorkflowGraph = ({
},
embedding: {
enabled: true,
validate (this, { parent }) {
const parentData = parent.getData()
return parentData.type === 'iteration' || parentData.type === 'loop'
validate (this) {
return false
}
},
translating: {

View File

@@ -107,6 +107,7 @@ const Workflow = forwardRef<WorkflowRef>((_props, ref) => {
copyEvent={copyEvent}
parseEvent={parseEvent}
config={config}
chatVariables={chatVariables}
/>
<Chat
ref={chatRef}