增加工具栏提示。
This commit is contained in:
parent
93d47eab40
commit
ca749aa7af
155
frontend/src/pages/Workflow/Definition/Design/constants.ts
Normal file
155
frontend/src/pages/Workflow/Definition/Design/constants.ts
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
// 节点端口配置
|
||||||
|
export const PORT_GROUPS = ['top', 'right', 'bottom', 'left'] as const;
|
||||||
|
|
||||||
|
// 默认样式配置
|
||||||
|
export const DEFAULT_STYLES = {
|
||||||
|
node: {
|
||||||
|
stroke: '#5F95FF',
|
||||||
|
strokeWidth: 2,
|
||||||
|
fill: '#FFF',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: 12,
|
||||||
|
fill: '#000000',
|
||||||
|
},
|
||||||
|
port: {
|
||||||
|
r: 4,
|
||||||
|
magnet: true,
|
||||||
|
stroke: '#5F95FF',
|
||||||
|
strokeWidth: 1,
|
||||||
|
fill: '#fff',
|
||||||
|
style: {
|
||||||
|
visibility: 'hidden',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
edge: {
|
||||||
|
stroke: '#1890ff',
|
||||||
|
strokeWidth: 2,
|
||||||
|
targetMarker: {
|
||||||
|
name: 'classic',
|
||||||
|
size: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 默认节点大小
|
||||||
|
export const NODE_SIZES = {
|
||||||
|
circle: {
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
},
|
||||||
|
rectangle: {
|
||||||
|
width: 100,
|
||||||
|
height: 50,
|
||||||
|
},
|
||||||
|
diamond: {
|
||||||
|
width: 60,
|
||||||
|
height: 60,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 生成端口组配置
|
||||||
|
export const generatePortGroups = () => {
|
||||||
|
const groups: Record<string, any> = {};
|
||||||
|
|
||||||
|
PORT_GROUPS.forEach(position => {
|
||||||
|
groups[position] = {
|
||||||
|
position,
|
||||||
|
attrs: {
|
||||||
|
circle: { ...DEFAULT_STYLES.port },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生成端口项配置
|
||||||
|
export const generatePortItems = () =>
|
||||||
|
PORT_GROUPS.map(group => ({ group }));
|
||||||
|
|
||||||
|
// 网格配置
|
||||||
|
export const GRID_CONFIG = {
|
||||||
|
size: 10,
|
||||||
|
visible: true,
|
||||||
|
type: 'dot',
|
||||||
|
args: {
|
||||||
|
color: '#a0a0a0',
|
||||||
|
thickness: 1,
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 连接配置
|
||||||
|
export const CONNECTING_CONFIG = {
|
||||||
|
router: 'manhattan',
|
||||||
|
connector: {
|
||||||
|
name: 'rounded',
|
||||||
|
args: {
|
||||||
|
radius: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
anchor: 'center',
|
||||||
|
connectionPoint: 'anchor',
|
||||||
|
allowBlank: false,
|
||||||
|
snap: true,
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 高亮配置
|
||||||
|
export const HIGHLIGHTING_CONFIG = {
|
||||||
|
magnetAvailable: {
|
||||||
|
name: 'stroke',
|
||||||
|
args: {
|
||||||
|
padding: 4,
|
||||||
|
attrs: {
|
||||||
|
strokeWidth: 4,
|
||||||
|
stroke: '#52c41a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
// 节点注册配置
|
||||||
|
export const NODE_REGISTRY_CONFIG = {
|
||||||
|
circle: {
|
||||||
|
inherit: 'circle',
|
||||||
|
width: NODE_SIZES.circle.width,
|
||||||
|
height: NODE_SIZES.circle.height,
|
||||||
|
attrs: {
|
||||||
|
body: DEFAULT_STYLES.node,
|
||||||
|
label: DEFAULT_STYLES.label,
|
||||||
|
},
|
||||||
|
ports: {
|
||||||
|
groups: generatePortGroups(),
|
||||||
|
items: generatePortItems(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
rectangle: {
|
||||||
|
inherit: 'rect',
|
||||||
|
width: NODE_SIZES.rectangle.width,
|
||||||
|
height: NODE_SIZES.rectangle.height,
|
||||||
|
attrs: {
|
||||||
|
body: DEFAULT_STYLES.node,
|
||||||
|
label: DEFAULT_STYLES.label,
|
||||||
|
},
|
||||||
|
ports: {
|
||||||
|
groups: generatePortGroups(),
|
||||||
|
items: generatePortItems(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
diamond: {
|
||||||
|
inherit: 'polygon',
|
||||||
|
width: NODE_SIZES.diamond.width,
|
||||||
|
height: NODE_SIZES.diamond.height,
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
...DEFAULT_STYLES.node,
|
||||||
|
refPoints: '0,10 10,0 20,10 10,20',
|
||||||
|
},
|
||||||
|
label: DEFAULT_STYLES.label,
|
||||||
|
},
|
||||||
|
ports: {
|
||||||
|
groups: generatePortGroups(),
|
||||||
|
items: generatePortItems(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
} as const;
|
||||||
@ -2,10 +2,17 @@ import React, { useEffect, useState, useRef } from 'react';
|
|||||||
import { useParams, useNavigate } from 'react-router-dom';
|
import { useParams, useNavigate } from 'react-router-dom';
|
||||||
import { Button, Space, Card, Row, Col } from 'antd';
|
import { Button, Space, Card, Row, Col } from 'antd';
|
||||||
import { ArrowLeftOutlined, SaveOutlined, PlayCircleOutlined } from '@ant-design/icons';
|
import { ArrowLeftOutlined, SaveOutlined, PlayCircleOutlined } from '@ant-design/icons';
|
||||||
import { Graph } from '@antv/x6';
|
import { Graph, Shape } from '@antv/x6';
|
||||||
import { getDefinitionDetail } from '../service';
|
import { getDefinitionDetail } from '../service';
|
||||||
import NodePanel from './components/NodePanel';
|
import NodePanel from './components/NodePanel';
|
||||||
import { NodeDefinition } from './types';
|
import { NodeDefinition } from './types';
|
||||||
|
import {
|
||||||
|
NODE_REGISTRY_CONFIG,
|
||||||
|
GRID_CONFIG,
|
||||||
|
CONNECTING_CONFIG,
|
||||||
|
HIGHLIGHTING_CONFIG,
|
||||||
|
DEFAULT_STYLES, generatePortGroups, generatePortItems,
|
||||||
|
} from './constants';
|
||||||
|
|
||||||
const WorkflowDesign: React.FC = () => {
|
const WorkflowDesign: React.FC = () => {
|
||||||
const { id } = useParams<{ id: string }>();
|
const { id } = useParams<{ id: string }>();
|
||||||
@ -22,52 +29,47 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (graphContainerRef.current) {
|
if (graphContainerRef.current) {
|
||||||
|
// 注册自定义节点
|
||||||
|
Object.entries(NODE_REGISTRY_CONFIG).forEach(([name, config]) => {
|
||||||
|
Graph.registerNode(name, config, true);
|
||||||
|
});
|
||||||
|
|
||||||
const graph = new Graph({
|
const graph = new Graph({
|
||||||
container: graphContainerRef.current,
|
container: graphContainerRef.current,
|
||||||
grid: {
|
grid: GRID_CONFIG,
|
||||||
size: 10,
|
|
||||||
visible: true,
|
|
||||||
type: 'dot',
|
|
||||||
args: {
|
|
||||||
color: '#a0a0a0',
|
|
||||||
thickness: 1,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
connecting: {
|
connecting: {
|
||||||
router: 'manhattan',
|
...CONNECTING_CONFIG,
|
||||||
connector: {
|
validateConnection({ sourceView, targetView, sourceMagnet, targetMagnet }) {
|
||||||
name: 'rounded',
|
if (!sourceMagnet || !targetMagnet) {
|
||||||
args: {
|
return false;
|
||||||
radius: 8,
|
}
|
||||||
|
if (sourceView === targetView) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
validateMagnet({ magnet }) {
|
||||||
|
const portGroup = magnet?.getAttribute('port-group');
|
||||||
|
return !!portGroup;
|
||||||
},
|
},
|
||||||
anchor: 'center',
|
|
||||||
connectionPoint: 'anchor',
|
|
||||||
allowBlank: false,
|
|
||||||
snap: true,
|
|
||||||
createEdge() {
|
createEdge() {
|
||||||
return this.createEdge({
|
return this.createEdge({
|
||||||
attrs: {
|
attrs: {
|
||||||
line: {
|
line: DEFAULT_STYLES.edge,
|
||||||
stroke: '#1890ff',
|
|
||||||
strokeWidth: 2,
|
|
||||||
targetMarker: {
|
|
||||||
name: 'classic',
|
|
||||||
size: 8,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
zIndex: -1,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
highlighting: {
|
highlighting: {
|
||||||
magnetAvailable: {
|
...HIGHLIGHTING_CONFIG,
|
||||||
|
magnetAdsorbed: {
|
||||||
name: 'stroke',
|
name: 'stroke',
|
||||||
args: {
|
args: {
|
||||||
padding: 4,
|
|
||||||
attrs: {
|
attrs: {
|
||||||
|
fill: '#fff',
|
||||||
|
stroke: '#31d0c6',
|
||||||
strokeWidth: 4,
|
strokeWidth: 4,
|
||||||
stroke: '#52c41a',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -82,6 +84,21 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 显示/隐藏连接桩
|
||||||
|
graph.on('node:mouseenter', ({ node }) => {
|
||||||
|
const ports = document.querySelectorAll(`[data-cell-id="${node.id}"] .x6-port-body`);
|
||||||
|
ports.forEach((port) => {
|
||||||
|
port.setAttribute('style', 'visibility: visible');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
graph.on('node:mouseleave', ({ node }) => {
|
||||||
|
const ports = document.querySelectorAll(`[data-cell-id="${node.id}"] .x6-port-body`);
|
||||||
|
ports.forEach((port) => {
|
||||||
|
port.setAttribute('style', 'visibility: hidden');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
setGraph(graph);
|
setGraph(graph);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -93,16 +110,22 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
const loadDefinitionDetail = async () => {
|
const loadDefinitionDetail = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await getDefinitionDetail(id);
|
const response = await getDefinitionDetail(id);
|
||||||
if (response.success) {
|
setTitle(`工作流设计 - ${response.name}`);
|
||||||
setTitle(`工作流设计 - ${response.data.name}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load workflow definition:', error);
|
console.error('Failed to load workflow definition:', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleNodeDragStart = (node: NodeDefinition, e: React.DragEvent) => {
|
const handleNodeDragStart = (node: NodeDefinition, e: React.DragEvent) => {
|
||||||
|
e.dataTransfer.setData('node', JSON.stringify(node));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDrop = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
if (graph) {
|
if (graph) {
|
||||||
|
const nodeData = e.dataTransfer.getData('node');
|
||||||
|
if (nodeData) {
|
||||||
|
const node = JSON.parse(nodeData);
|
||||||
const { clientX, clientY } = e;
|
const { clientX, clientY } = e;
|
||||||
const point = graph.clientToLocal({ x: clientX, y: clientY });
|
const point = graph.clientToLocal({ x: clientX, y: clientY });
|
||||||
|
|
||||||
@ -123,74 +146,8 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
ports: {
|
ports: {
|
||||||
groups: {
|
groups: generatePortGroups(),
|
||||||
top: {
|
items: generatePortItems(),
|
||||||
position: 'top',
|
|
||||||
attrs: {
|
|
||||||
circle: {
|
|
||||||
r: 4,
|
|
||||||
magnet: true,
|
|
||||||
stroke: '#5F95FF',
|
|
||||||
strokeWidth: 1,
|
|
||||||
fill: '#fff',
|
|
||||||
style: {
|
|
||||||
visibility: 'hidden',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
right: {
|
|
||||||
position: 'right',
|
|
||||||
attrs: {
|
|
||||||
circle: {
|
|
||||||
r: 4,
|
|
||||||
magnet: true,
|
|
||||||
stroke: '#5F95FF',
|
|
||||||
strokeWidth: 1,
|
|
||||||
fill: '#fff',
|
|
||||||
style: {
|
|
||||||
visibility: 'hidden',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
bottom: {
|
|
||||||
position: 'bottom',
|
|
||||||
attrs: {
|
|
||||||
circle: {
|
|
||||||
r: 4,
|
|
||||||
magnet: true,
|
|
||||||
stroke: '#5F95FF',
|
|
||||||
strokeWidth: 1,
|
|
||||||
fill: '#fff',
|
|
||||||
style: {
|
|
||||||
visibility: 'hidden',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
left: {
|
|
||||||
position: 'left',
|
|
||||||
attrs: {
|
|
||||||
circle: {
|
|
||||||
r: 4,
|
|
||||||
magnet: true,
|
|
||||||
stroke: '#5F95FF',
|
|
||||||
strokeWidth: 1,
|
|
||||||
fill: '#fff',
|
|
||||||
style: {
|
|
||||||
visibility: 'hidden',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{ group: 'top' },
|
|
||||||
{ group: 'right' },
|
|
||||||
{ group: 'bottom' },
|
|
||||||
{ group: 'left' },
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -200,6 +157,11 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
y: point.y,
|
y: point.y,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragOver = (e: React.DragEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -253,6 +215,8 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
width: '100%',
|
width: '100%',
|
||||||
height: '100%',
|
height: '100%',
|
||||||
}}
|
}}
|
||||||
|
onDrop={handleDrop}
|
||||||
|
onDragOver={handleDragOver}
|
||||||
/>
|
/>
|
||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user