Merge pull request #159 from SuanmoSuanyangTechnology/feature/ui_zy

Feature/UI zy
This commit is contained in:
yingzhao
2026-01-20 16:15:45 +08:00
committed by GitHub
13 changed files with 78 additions and 220 deletions

View File

@@ -0,0 +1,13 @@
import yaml from 'js-yaml';
export const exportToYaml = (data: unknown, filename: string = 'export.yaml') => {
const yamlStr = yaml.dump(data);
const blob = new Blob([yamlStr], { type: 'text/yaml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
};

View File

@@ -221,12 +221,12 @@ const Agent = forwardRef<AgentRef>((_props, ref) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
saveAgentConfig(data.app_id, params) saveAgentConfig(data.app_id, params)
.then(() => { .then((res) => {
if (flag) { if (flag) {
message.success(t('common.saveSuccess')) message.success(t('common.saveSuccess'))
} }
setIsSave(false) setIsSave(false)
resolve(true) resolve(res)
}).catch(error => { }).catch(error => {
reject(error) reject(error)
}) })

View File

@@ -58,16 +58,14 @@ const Cluster = forwardRef<ClusterRef>((_props, ref) => {
})) }))
} }
console.log('params', params)
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
form.validateFields().then(() => { form.validateFields().then(() => {
saveMultiAgentConfig(id as string, params) saveMultiAgentConfig(id as string, params)
.then(() => { .then((res) => {
if (flag) { if (flag) {
message.success(t('common.saveSuccess')) message.success(t('common.saveSuccess'))
} }
resolve(true) resolve(res)
}) })
.catch(error => { .catch(error => {
reject(error) reject(error)

View File

@@ -11,7 +11,7 @@ import exportIcon from '@/assets/images/export_hover.svg'
import deleteIcon from '@/assets/images/delete_hover.svg' import deleteIcon from '@/assets/images/delete_hover.svg'
import type { Application, ApplicationModalRef } from '@/views/ApplicationManagement/types'; import type { Application, ApplicationModalRef } from '@/views/ApplicationManagement/types';
import ApplicationModal from '@/views/ApplicationManagement/components/ApplicationModal' import ApplicationModal from '@/views/ApplicationManagement/components/ApplicationModal'
import type { CopyModalRef, WorkflowRef } from '../types' import type { CopyModalRef, AgentRef, ClusterRef, WorkflowRef } from '../types'
import { deleteApplication } from '@/api/application' import { deleteApplication } from '@/api/application'
import CopyModal from './CopyModal' import CopyModal from './CopyModal'
@@ -30,10 +30,11 @@ interface ConfigHeaderProps {
handleChangeTab: (key: string) => void; handleChangeTab: (key: string) => void;
refresh: () => void; refresh: () => void;
workflowRef: React.RefObject<WorkflowRef> workflowRef: React.RefObject<WorkflowRef>
appRef?: React.RefObject<AgentRef | ClusterRef | WorkflowRef>
} }
const ConfigHeader: FC<ConfigHeaderProps> = ({ const ConfigHeader: FC<ConfigHeaderProps> = ({
application, activeTab, handleChangeTab, refresh, application, activeTab, handleChangeTab, refresh,
workflowRef workflowRef,
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigate = useNavigate();
@@ -48,7 +49,7 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
})) }))
} }
const formatMenuItems = () => { const formatMenuItems = () => {
const items = ['edit', 'copy', 'delete'].map(key => ({ const items = ['edit', 'copy', 'export', 'delete'].map(key => ({
key, key,
icon: <img src={menuIcons[key]} className="rb:w-4 rb:h-4 rb:mr-2" />, icon: <img src={menuIcons[key]} className="rb:w-4 rb:h-4 rb:mr-2" />,
label: t(`common.${key}`), label: t(`common.${key}`),
@@ -59,7 +60,6 @@ const ConfigHeader: FC<ConfigHeaderProps> = ({
} }
} }
const handleClick: MenuProps['onClick'] = ({ key }) => { const handleClick: MenuProps['onClick'] = ({ key }) => {
console.log('key', key)
switch (key) { switch (key) {
case 'edit': case 'edit':
applicationModalRef.current?.handleOpen(application as Application) applicationModalRef.current?.handleOpen(application as Application)

View File

@@ -60,10 +60,11 @@ const ApplicationConfig: React.FC = () => {
handleChangeTab={handleChangeTab} handleChangeTab={handleChangeTab}
application={application as Application} application={application as Application}
refresh={getApplicationInfo} refresh={getApplicationInfo}
appRef={application?.type === 'agent' ? agentRef : application?.type === 'multi_agent' ? clusterRef : application?.type === 'workflow' ? workflowRef : undefined}
workflowRef={workflowRef} workflowRef={workflowRef}
/> />
{activeTab === 'arrangement' && application?.type === 'agent' && <Agent ref={agentRef} />} {activeTab === 'arrangement' && application?.type === 'agent' && <Agent ref={agentRef} />}
{activeTab === 'arrangement' && application?.type === 'multi_agent' && <Cluster ref={clusterRef} application={application as Application} />} {activeTab === 'arrangement' && application?.type === 'multi_agent' && <Cluster ref={clusterRef} />}
{activeTab === 'arrangement' && application?.type === 'workflow' && <Workflow ref={workflowRef} />} {activeTab === 'arrangement' && application?.type === 'workflow' && <Workflow ref={workflowRef} />}
{activeTab === 'api' && <Api application={application} />} {activeTab === 'api' && <Api application={application} />}
{activeTab === 'release' && <ReleasePage data={application as Application} refresh={getApplicationInfo} />} {activeTab === 'release' && <ReleasePage data={application as Application} refresh={getApplicationInfo} />}

View File

@@ -2,7 +2,7 @@ import { useState } from 'react';
import { Popover } from 'antd'; import { Popover } from 'antd';
import clsx from 'clsx'; import clsx from 'clsx';
import type { ReactShapeConfig } from '@antv/x6-react-shape'; import type { ReactShapeConfig } from '@antv/x6-react-shape';
import { nodeLibrary, graphNodeLibrary } from '../../constant'; import { nodeLibrary, graphNodeLibrary, edgeAttrs } from '../../constant';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => { const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
@@ -47,7 +47,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
graph.addEdge({ graph.addEdge({
source: { cell: edge.getSourceCellId(), port: edge.getSourcePortId() }, source: { cell: edge.getSourceCellId(), port: edge.getSourcePortId() },
target: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'left')?.id || 'left' }, target: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'left')?.id || 'left' },
attrs: edge.getAttrs(), ...edgeAttrs
}); });
}); });
@@ -57,7 +57,7 @@ const AddNode: ReactShapeConfig['component'] = ({ node, graph }) => {
graph.addEdge({ graph.addEdge({
source: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'right')?.id || 'right' }, source: { cell: newNode.id, port: newNode.getPorts().find((port: any) => port.group === 'right')?.id || 'right' },
target: { cell: edge.getTargetCellId(), port: targetPortId }, target: { cell: edge.getTargetCellId(), port: targetPortId },
attrs: edge.getAttrs(), ...edgeAttrs
}); });
}); });

View File

@@ -2,9 +2,7 @@ import { useEffect } from 'react';
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import clsx from 'clsx'; import clsx from 'clsx';
import type { ReactShapeConfig } from '@antv/x6-react-shape'; import type { ReactShapeConfig } from '@antv/x6-react-shape';
import { graphNodeLibrary } from '../../constant'; import { graphNodeLibrary, edgeAttrs } from '../../constant';
import { edge_color } from '../../hooks/useWorkflowGraph'
const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => { const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
const data = node.getData() || {}; const data = node.getData() || {};
@@ -56,16 +54,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
graph.addEdge({ graph.addEdge({
source: { cell: cycleStartNode.id, port: sourcePort }, source: { cell: cycleStartNode.id, port: sourcePort },
target: { cell: addNode.id, port: targetPort }, target: { cell: addNode.id, port: targetPort },
attrs: { ...edgeAttrs,
line: {
stroke: edge_color,
strokeWidth: 1,
targetMarker: {
name: 'block',
size: 8,
},
},
},
}); });
} }
} }
@@ -122,16 +111,7 @@ const LoopNode: ReactShapeConfig['component'] = ({ node, graph }) => {
cell: addNode.id, cell: addNode.id,
port: targetPorts.find((port: any) => port.group === 'left')?.id || 'left' port: targetPorts.find((port: any) => port.group === 'left')?.id || 'left'
}, },
attrs: { ...edgeAttrs
line: {
stroke: edge_color,
strokeWidth: 1,
targetMarker: {
name: 'block',
size: 2,
},
},
},
} }
graph.addEdge(edgeConfig) graph.addEdge(edgeConfig)
} }

View File

@@ -1,7 +1,7 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { Popover } from 'antd'; import { Popover } from 'antd';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { nodeLibrary, graphNodeLibrary } from '../constant'; import { nodeLibrary, graphNodeLibrary, edgeAttrs } from '../constant';
interface PortClickHandlerProps { interface PortClickHandlerProps {
graph: any; graph: any;
@@ -149,16 +149,7 @@ const PortClickHandler: React.FC<PortClickHandlerProps> = ({ graph }) => {
graph.addEdge({ graph.addEdge({
source: { cell: sourceNode.id, port: sourcePort }, source: { cell: sourceNode.id, port: sourcePort },
target: { cell: newNode.id, port: targetPort }, target: { cell: newNode.id, port: targetPort },
attrs: { ...edgeAttrs
line: {
stroke: '#155EEF',
strokeWidth: 1,
targetMarker: {
name: 'block',
size: 8,
},
},
},
// zIndex: sourceNodeData.cycle && sourceNodeType == 'cycle-start' ? 1 : sourceNodeData.cycle ? 2 : 0 // zIndex: sourceNodeData.cycle && sourceNodeType == 'cycle-start' ? 1 : sourceNodeData.cycle ? 2 : 0
}); });

View File

@@ -6,6 +6,7 @@ import { Form, Button, Select, Space, Divider, InputNumber, Radio, type SelectPr
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin' import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
import VariableSelect from '../VariableSelect' import VariableSelect from '../VariableSelect'
import Editor from '../../Editor' import Editor from '../../Editor'
import { edgeAttrs } from '../../../constant'
interface CaseListProps { interface CaseListProps {
value?: Array<{ logical_operator: 'and' | 'or'; expressions: { left: string; operator: string; right: string; input_type?: string; }[] }>; value?: Array<{ logical_operator: 'and' | 'or'; expressions: { left: string; operator: string; right: string; input_type?: string; }[] }>;
@@ -120,16 +121,7 @@ const CaseList: FC<CaseListProps> = ({
graphRef.current?.addEdge({ graphRef.current?.addEdge({
source: { cell: sourceCellId, port: sourcePortId }, source: { cell: sourceCellId, port: sourcePortId },
target: { cell: selectedNode.id, port: targetPortId }, target: { cell: selectedNode.id, port: targetPortId },
attrs: { ...edgeAttrs,
line: {
stroke: '#155EEF',
strokeWidth: 1,
targetMarker: {
name: 'block',
size: 8,
},
},
},
}); });
} }
graphRef.current?.removeCell(edge); graphRef.current?.removeCell(edge);
@@ -174,16 +166,7 @@ const CaseList: FC<CaseListProps> = ({
graphRef.current?.addEdge({ graphRef.current?.addEdge({
source: { cell: selectedNode.id, port: newPortId }, source: { cell: selectedNode.id, port: newPortId },
target: { cell: targetCellId, port: targetPortId }, target: { cell: targetCellId, port: targetPortId },
attrs: { ...edgeAttrs
line: {
stroke: '#155EEF',
strokeWidth: 1,
targetMarker: {
name: 'block',
size: 8,
},
},
},
}); });
} }
} }

View File

@@ -5,6 +5,7 @@ import { Graph, Node } from '@antv/x6';
import Editor from '../../Editor'; import Editor from '../../Editor';
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin' import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
import { edgeAttrs } from '../../../constant'
interface CategoryListProps { interface CategoryListProps {
parentName: string; parentName: string;
@@ -70,16 +71,7 @@ const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRe
graphRef.current?.addEdge({ graphRef.current?.addEdge({
source: { cell: sourceCellId, port: sourcePortId }, source: { cell: sourceCellId, port: sourcePortId },
target: { cell: selectedNode.id, port: targetPortId }, target: { cell: selectedNode.id, port: targetPortId },
attrs: { ...edgeAttrs
line: {
stroke: '#155EEF',
strokeWidth: 1,
targetMarker: {
name: 'block',
size: 8,
},
},
},
}); });
} }
return; return;
@@ -110,16 +102,7 @@ const CategoryList: FC<CategoryListProps> = ({ parentName, selectedNode, graphRe
graphRef.current?.addEdge({ graphRef.current?.addEdge({
source: { cell: selectedNode.id, port: newPortId }, source: { cell: selectedNode.id, port: newPortId },
target: { cell: targetCellId, port: targetPortId }, target: { cell: targetCellId, port: targetPortId },
attrs: { ...edgeAttrs
line: {
stroke: '#155EEF',
strokeWidth: 1,
targetMarker: {
name: 'block',
size: 8,
},
},
},
}); });
} }
} }

View File

@@ -1,6 +1,6 @@
import { type FC } from 'react' import { type FC } from 'react'
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { Form, Select, Input, Button } from 'antd' import { Form, Select, Input, Button, InputNumber } from 'antd'
import VariableSelect from '../VariableSelect' import VariableSelect from '../VariableSelect'
import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin' import type { Suggestion } from '../../Editor/plugin/AutocompletePlugin'
@@ -93,6 +93,7 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
</Button> </Button>
</div> </div>
{fields.map(({ key, name }, index) => { {fields.map(({ key, name }, index) => {
const currentType = value?.[index]?.type;
const currentInputType = value?.[index]?.input_type; const currentInputType = value?.[index]?.input_type;
return ( return (
@@ -131,7 +132,8 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
</div> </div>
<Form.Item name={[name, 'value']} noStyle> <Form.Item name={[name, 'value']} noStyle>
{currentInputType === 'variable' ? ( {currentInputType === 'variable'
? (
<VariableSelect <VariableSelect
placeholder={t('common.pleaseSelect')} placeholder={t('common.pleaseSelect')}
options={availableOptions.filter(option => { options={availableOptions.filter(option => {
@@ -143,7 +145,15 @@ const CycleVarsList: FC<CycleVarsListProps> = ({
variant="borderless" variant="borderless"
size="small" size="small"
/> />
) : ( )
: currentType === 'number'
? <InputNumber
placeholder={t('common.pleaseEnter')}
variant="borderless"
className="rb:w-full! rb:my-1!"
onChange={(value) => form.setFieldValue([name, 'value'], value)}
/>
: (
<Input.TextArea <Input.TextArea
placeholder={t('common.pleaseEnter')} placeholder={t('common.pleaseEnter')}
rows={3} rows={3}

View File

@@ -517,6 +517,8 @@ interface NodeConfig {
ports?: PortsConfig; ports?: PortsConfig;
} }
export const edge_color = '#155EEF';
export const edge_selected_color = '#4DA8FF'
// 统一的端口 markup 配置 // 统一的端口 markup 配置
export const portMarkup = [ export const portMarkup = [
{ {
@@ -534,9 +536,9 @@ export const portAttrs = {
body: { body: {
r: 6, r: 6,
magnet: true, magnet: true,
stroke: '#155EEF', stroke: edge_color,
strokeWidth: 2, strokeWidth: 2,
fill: '#155EEF', fill: edge_color,
}, },
label: { label: {
text: '+', text: '+',
@@ -777,3 +779,17 @@ export const outputVariable: { [key: string]: OutputVariable } = {
], ],
}, },
} }
export const edgeAttrs = {
attrs: {
line: {
stroke: edge_color,
strokeWidth: 1,
targetMarker: {
name: 'block',
width: 4,
height: 4,
},
},
},
}

View File

@@ -5,7 +5,7 @@ import { App } from 'antd'
import { Graph, Node, MiniMap, Snapline, Clipboard, Keyboard, type Edge } from '@antv/x6'; import { Graph, Node, MiniMap, Snapline, Clipboard, Keyboard, type Edge } from '@antv/x6';
import { register } from '@antv/x6-react-shape'; import { register } from '@antv/x6-react-shape';
import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs } from '../constant'; import { nodeRegisterLibrary, graphNodeLibrary, nodeLibrary, portMarkup, portAttrs, edgeAttrs, edge_color, edge_selected_color } from '../constant';
import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types'; import type { WorkflowConfig, NodeProperties, ChatVariable } from '../types';
import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application' import { getWorkflowConfig, saveWorkflowConfig } from '@/api/application'
import type { PortMetadata } from '@antv/x6/lib/model/port'; import type { PortMetadata } from '@antv/x6/lib/model/port';
@@ -23,12 +23,8 @@ export interface UseWorkflowGraphReturn {
setSelectedNode: React.Dispatch<React.SetStateAction<Node | null>>; setSelectedNode: React.Dispatch<React.SetStateAction<Node | null>>;
zoomLevel: number; zoomLevel: number;
setZoomLevel: React.Dispatch<React.SetStateAction<number>>; setZoomLevel: React.Dispatch<React.SetStateAction<number>>;
canUndo: boolean;
canRedo: boolean;
isHandMode: boolean; isHandMode: boolean;
setIsHandMode: React.Dispatch<React.SetStateAction<boolean>>; setIsHandMode: React.Dispatch<React.SetStateAction<boolean>>;
onUndo: () => void;
onRedo: () => void;
onDrop: (event: React.DragEvent) => void; onDrop: (event: React.DragEvent) => void;
blankClick: () => void; blankClick: () => void;
deleteEvent: () => boolean | void; deleteEvent: () => boolean | void;
@@ -39,8 +35,6 @@ export interface UseWorkflowGraphReturn {
setChatVariables: React.Dispatch<React.SetStateAction<ChatVariable[]>>; setChatVariables: React.Dispatch<React.SetStateAction<ChatVariable[]>>;
} }
export const edge_color = '#155EEF';
const edge_selected_color = '#4DA8FF'
export const useWorkflowGraph = ({ export const useWorkflowGraph = ({
containerRef, containerRef,
miniMapRef, miniMapRef,
@@ -51,9 +45,6 @@ export const useWorkflowGraph = ({
const graphRef = useRef<Graph>(); const graphRef = useRef<Graph>();
const [selectedNode, setSelectedNode] = useState<Node | null>(null); const [selectedNode, setSelectedNode] = useState<Node | null>(null);
const [zoomLevel, setZoomLevel] = useState(1); const [zoomLevel, setZoomLevel] = useState(1);
const historyRef = useRef<{ undoStack: string[], redoStack: string[] }>({ undoStack: [], redoStack: [] });
const [canUndo, setCanUndo] = useState(false);
const [canRedo, setCanRedo] = useState(false);
const [isHandMode, setIsHandMode] = useState(true); const [isHandMode, setIsHandMode] = useState(true);
const [config, setConfig] = useState<WorkflowConfig | null>(null); const [config, setConfig] = useState<WorkflowConfig | null>(null);
const [chatVariables, setChatVariables] = useState<ChatVariable[]>([]) const [chatVariables, setChatVariables] = useState<ChatVariable[]>([])
@@ -338,17 +329,7 @@ export const useWorkflowGraph = ({
port: targetPorts.find((port: any) => port.group === 'left')?.id || 'left' port: targetPorts.find((port: any) => port.group === 'left')?.id || 'left'
}, },
connector: { name: 'smooth' }, connector: { name: 'smooth' },
attrs: { ...edgeAttrs
line: {
stroke: edge_color,
strokeWidth: 1,
targetMarker: {
name: 'diamond',
width: 4,
height: 4,
},
},
},
// zIndex: loopIterationCount // zIndex: loopIterationCount
} }
@@ -368,48 +349,6 @@ export const useWorkflowGraph = ({
}, 200) }, 200)
} }
} }
const saveState = () => {
if (!graphRef.current) return;
const state = JSON.stringify(graphRef.current.toJSON());
historyRef.current.undoStack.push(state);
historyRef.current.redoStack = [];
if (historyRef.current.undoStack.length > 50) {
historyRef.current.undoStack.shift();
}
updateHistoryState();
};
const updateHistoryState = () => {
setCanUndo(historyRef.current.undoStack.length > 1);
setCanRedo(historyRef.current.redoStack.length > 0);
};
// 撤销
const onUndo = () => {
if (!graphRef.current || historyRef.current.undoStack.length === 0) return;
const { undoStack = [], redoStack = [] } = historyRef.current
const currentState = JSON.stringify(graphRef.current.toJSON());
const prevState = undoStack[undoStack.length - 2];
historyRef.current.redoStack = [...redoStack, currentState]
historyRef.current.undoStack = undoStack.slice(0, undoStack.length - 1)
graphRef.current.fromJSON(JSON.parse(prevState));
updateHistoryState();
};
// 重做
const onRedo = () => {
if (!graphRef.current || historyRef.current.redoStack.length === 0) return;
const { undoStack = [], redoStack = [] } = historyRef.current
const nextState = redoStack[redoStack.length - 1];
historyRef.current.undoStack = [...undoStack, nextState]
historyRef.current.redoStack = redoStack.slice(0, redoStack.length - 1)
graphRef.current.fromJSON(JSON.parse(nextState));
updateHistoryState();
};
// 使用插件 // 使用插件
const setupPlugins = () => { const setupPlugins = () => {
if (!graphRef.current || !miniMapRef.current) return; if (!graphRef.current || !miniMapRef.current) return;
@@ -563,20 +502,6 @@ export const useWorkflowGraph = ({
} }
return false; return false;
}; };
// 撤销快捷键事件
const undoEvent = () => {
if (canUndo) {
onUndo();
}
return false;
};
// 重做快捷键事件
const redoEvent = () => {
if (canRedo) {
onRedo();
}
return false;
};
// 删除选中的节点和连线事件 // 删除选中的节点和连线事件
const deleteEvent = () => { const deleteEvent = () => {
if (!graphRef.current) return; if (!graphRef.current) return;
@@ -748,8 +673,6 @@ export const useWorkflowGraph = ({
background: { background: {
color: '#F0F3F8', color: '#F0F3F8',
}, },
// width: container.clientWidth || 800,
// height: container.clientHeight || 600,
autoResize: true, autoResize: true,
grid: { grid: {
visible: true, visible: true,
@@ -765,37 +688,26 @@ export const useWorkflowGraph = ({
enabled: true, enabled: true,
}, },
connecting: { connecting: {
// router: 'orth',
// router: 'manhattan',
connector: { connector: {
name: 'smooth', name: 'smooth',
args: { args: {
radius: 8, radius: 8,
}, },
}, },
anchor: 'center', anchor: 'midSide',
connectionPoint: 'anchor', connectionPoint: 'anchor',
allowBlank: false, allowBlank: false,
allowLoop: false,
allowNode: false, allowNode: false,
allowEdge: false, allowEdge: false,
allowPort: true,
allowMulti: true,
highlight: true, highlight: true,
snap: { snap: {
radius: 20, radius: 20,
}, },
createEdge() { createEdge() {
return graphRef.current?.createEdge({ return graphRef.current?.createEdge(edgeAttrs);
attrs: {
line: {
stroke: edge_color,
strokeWidth: 1,
targetMarker: {
name: 'diamond',
width: 4,
height: 4,
},
},
},
});
}, },
validateConnection({ sourceCell, targetCell, targetMagnet }) { validateConnection({ sourceCell, targetCell, targetMagnet }) {
if (!targetMagnet) return false; if (!targetMagnet) return false;
@@ -901,27 +813,7 @@ export const useWorkflowGraph = ({
// 监听缩放事件 // 监听缩放事件
graphRef.current.on('scale', scaleEvent); graphRef.current.on('scale', scaleEvent);
// 监听节点移动事件 // 监听节点移动事件
// graphRef.current.on('node:moved', nodeMoved); graphRef.current.on('node:moved', nodeMoved);
graphRef.current.on('node:change:position', nodeChangePosition);
// 监听画布变化事件
const events = [
'node:added',
'node:removed',
'edge:added',
'edge:removed',
];
events.forEach(event => {
graphRef.current!.on(event, () => {
console.log('event', event);
setTimeout(() => saveState(), 50);
});
});
// 监听撤销键盘事件
graphRef.current.bindKey(['ctrl+z', 'cmd+z'], undoEvent);
// 监听重做键盘事件
graphRef.current.bindKey(['ctrl+shift+z', 'cmd+shift+z', 'ctrl+y', 'cmd+y'], redoEvent);
// 监听复制键盘事件 // 监听复制键盘事件
graphRef.current.bindKey(['ctrl+c', 'cmd+c'], copyEvent); graphRef.current.bindKey(['ctrl+c', 'cmd+c'], copyEvent);
// 监听粘贴键盘事件 // 监听粘贴键盘事件
@@ -929,11 +821,6 @@ export const useWorkflowGraph = ({
// 删除选中的节点和连线 // 删除选中的节点和连线
graphRef.current.bindKey(['ctrl+d', 'cmd+d', 'delete', 'backspace'], deleteEvent); graphRef.current.bindKey(['ctrl+d', 'cmd+d', 'delete', 'backspace'], deleteEvent);
// 保存初始状态
setTimeout(() => saveState(), 100);
// init window hook
(window as Window & { __x6_instances__?: Graph[] }).__x6_instances__ = [];
(window as Window & { __x6_instances__?: Graph[] }).__x6_instances__?.push(graphRef.current);
}; };
useEffect(() => { useEffect(() => {
@@ -1146,11 +1033,11 @@ export const useWorkflowGraph = ({
}), }),
} }
saveWorkflowConfig(config.app_id, params as WorkflowConfig) saveWorkflowConfig(config.app_id, params as WorkflowConfig)
.then(() => { .then((res) => {
if (flag) { if (flag) {
message.success(t('common.saveSuccess')) message.success(t('common.saveSuccess'))
} }
resolve(true) resolve(res)
}).catch(error => { }).catch(error => {
reject(error) reject(error)
}) })
@@ -1165,12 +1052,8 @@ export const useWorkflowGraph = ({
setSelectedNode, setSelectedNode,
zoomLevel, zoomLevel,
setZoomLevel, setZoomLevel,
canUndo,
canRedo,
isHandMode, isHandMode,
setIsHandMode, setIsHandMode,
onUndo,
onRedo,
onDrop, onDrop,
blankClick, blankClick,
deleteEvent, deleteEvent,