1
This commit is contained in:
parent
978a24790a
commit
fa506b4d96
@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React from 'react';
|
||||||
import { Modal, Form, Input, InputNumber, Radio } from 'antd';
|
import { Modal, Form, Input, InputNumber, Radio } from 'antd';
|
||||||
import { Edge } from '@antv/x6';
|
import { Edge } from '@antv/x6';
|
||||||
import { ConditionType, EdgeCondition } from '../types';
|
import { EdgeCondition } from '../nodes/types';
|
||||||
|
|
||||||
interface ExpressionModalProps {
|
interface ExpressionModalProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
|
|||||||
@ -45,8 +45,17 @@ const NodeConfigModal: React.FC<NodeConfigModalProps> = ({
|
|||||||
if (nodeDefinition && node) {
|
if (nodeDefinition && node) {
|
||||||
// 从节点数据中获取现有配置
|
// 从节点数据中获取现有配置
|
||||||
const nodeData = node.getData() || {};
|
const nodeData = node.getData() || {};
|
||||||
|
|
||||||
|
// 准备默认的基本信息配置
|
||||||
|
const defaultConfig = {
|
||||||
|
nodeName: nodeDefinition.nodeName, // 默认节点名称
|
||||||
|
nodeCode: nodeDefinition.nodeCode, // 默认节点编码
|
||||||
|
description: nodeDefinition.description // 默认节点描述
|
||||||
|
};
|
||||||
|
|
||||||
|
// 合并默认值和已保存的配置
|
||||||
setFormData({
|
setFormData({
|
||||||
config: nodeData.config || {},
|
config: { ...defaultConfig, ...(nodeData.config || {}) },
|
||||||
inputMapping: nodeData.inputMapping || {},
|
inputMapping: nodeData.inputMapping || {},
|
||||||
outputMapping: nodeData.outputMapping || {},
|
outputMapping: nodeData.outputMapping || {},
|
||||||
});
|
});
|
||||||
@ -92,6 +101,7 @@ const NodeConfigModal: React.FC<NodeConfigModalProps> = ({
|
|||||||
children: (
|
children: (
|
||||||
<div style={{ padding: '16px 0' }}>
|
<div style={{ padding: '16px 0' }}>
|
||||||
<BetaSchemaForm
|
<BetaSchemaForm
|
||||||
|
key={`config-${node?.id}-${JSON.stringify(formData.config)}`}
|
||||||
layoutType="Form"
|
layoutType="Form"
|
||||||
columns={convertJsonSchemaToColumns(nodeDefinition.configSchema as any)}
|
columns={convertJsonSchemaToColumns(nodeDefinition.configSchema as any)}
|
||||||
initialValues={formData.config}
|
initialValues={formData.config}
|
||||||
@ -112,6 +122,7 @@ const NodeConfigModal: React.FC<NodeConfigModalProps> = ({
|
|||||||
children: (
|
children: (
|
||||||
<div style={{ padding: '16px 0' }}>
|
<div style={{ padding: '16px 0' }}>
|
||||||
<BetaSchemaForm
|
<BetaSchemaForm
|
||||||
|
key={`input-${node?.id}-${JSON.stringify(formData.inputMapping)}`}
|
||||||
layoutType="Form"
|
layoutType="Form"
|
||||||
columns={convertJsonSchemaToColumns(nodeDefinition.inputMappingSchema as any)}
|
columns={convertJsonSchemaToColumns(nodeDefinition.inputMappingSchema as any)}
|
||||||
initialValues={formData.inputMapping}
|
initialValues={formData.inputMapping}
|
||||||
@ -130,6 +141,7 @@ const NodeConfigModal: React.FC<NodeConfigModalProps> = ({
|
|||||||
children: (
|
children: (
|
||||||
<div style={{ padding: '16px 0' }}>
|
<div style={{ padding: '16px 0' }}>
|
||||||
<BetaSchemaForm
|
<BetaSchemaForm
|
||||||
|
key={`output-${node?.id}-${JSON.stringify(formData.outputMapping)}`}
|
||||||
layoutType="Form"
|
layoutType="Form"
|
||||||
columns={convertJsonSchemaToColumns(nodeDefinition.outputMappingSchema as any)}
|
columns={convertJsonSchemaToColumns(nodeDefinition.outputMappingSchema as any)}
|
||||||
initialValues={formData.outputMapping}
|
initialValues={formData.outputMapping}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React, {useState, useEffect} from 'react';
|
import React, {useState, useEffect} from 'react';
|
||||||
import {Card, Collapse, Tooltip, message} from 'antd';
|
import {Card, Collapse, Tooltip, message} from 'antd';
|
||||||
import type {NodeCategory} from '../types';
|
import type {NodeCategory} from '../nodes/types';
|
||||||
import {
|
import {
|
||||||
PlayCircleOutlined,
|
PlayCircleOutlined,
|
||||||
StopOutlined,
|
StopOutlined,
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import NodePanel from './NodePanel';
|
import NodePanel from './NodePanel';
|
||||||
import type { NodeDefinitionResponse } from "../nodes/nodeService";
|
import type { WorkflowNodeDefinition } from "../nodes/nodeService";
|
||||||
|
|
||||||
interface WorkflowCanvasProps {
|
interface WorkflowCanvasProps {
|
||||||
graphContainerRef: React.RefObject<HTMLDivElement>;
|
graphContainerRef: React.RefObject<HTMLDivElement>;
|
||||||
|
|||||||
@ -1,150 +0,0 @@
|
|||||||
// 节点端口配置
|
|
||||||
export const PORT_GROUPS = ['top', 'right', 'bottom', 'left'] as const;
|
|
||||||
|
|
||||||
// 转换后端端口配置为X6格式
|
|
||||||
export const convertPortConfig = (ports: any = {}) => {
|
|
||||||
const { groups = {} } = ports;
|
|
||||||
|
|
||||||
// 转换groups配置
|
|
||||||
const processedGroups: any = {};
|
|
||||||
Object.entries(groups).forEach(([key, group]: [string, any]) => {
|
|
||||||
processedGroups[key] = {
|
|
||||||
...group,
|
|
||||||
attrs: {
|
|
||||||
circle: {
|
|
||||||
...group.attrs?.circle,
|
|
||||||
magnet: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
position: group.position
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// 如果没有types,就用groups的key作为默认的items
|
|
||||||
const items = Object.keys(groups).map(group => ({
|
|
||||||
group: group
|
|
||||||
}));
|
|
||||||
|
|
||||||
return {
|
|
||||||
groups: processedGroups,
|
|
||||||
items
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// 默认样式配置
|
|
||||||
export const DEFAULT_STYLES = {
|
|
||||||
node: {
|
|
||||||
stroke: '#5F95FF',
|
|
||||||
strokeWidth: 2,
|
|
||||||
fill: '#FFF',
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
fontSize: 12,
|
|
||||||
fill: '#000000',
|
|
||||||
},
|
|
||||||
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 GRID_CONFIG = {
|
|
||||||
size: 10,
|
|
||||||
visible: true,
|
|
||||||
type: 'dot',
|
|
||||||
args: {
|
|
||||||
color: '#a0a0a0',
|
|
||||||
thickness: 1,
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
|
|
||||||
// 连接配置
|
|
||||||
export const CONNECTING_CONFIG = {
|
|
||||||
snap: true,
|
|
||||||
allowBlank: false,
|
|
||||||
allowLoop: false,
|
|
||||||
highlight: true,
|
|
||||||
connector: 'rounded',
|
|
||||||
connectionPoint: 'boundary',
|
|
||||||
router: {
|
|
||||||
name: 'manhattan'
|
|
||||||
},
|
|
||||||
validateConnection({
|
|
||||||
sourceMagnet,
|
|
||||||
targetMagnet
|
|
||||||
}: any) {
|
|
||||||
if (!sourceMagnet || !targetMagnet) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return 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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
rectangle: {
|
|
||||||
inherit: 'rect',
|
|
||||||
width: NODE_SIZES.rectangle.width,
|
|
||||||
height: NODE_SIZES.rectangle.height,
|
|
||||||
attrs: {
|
|
||||||
body: DEFAULT_STYLES.node,
|
|
||||||
label: DEFAULT_STYLES.label,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} as const;
|
|
||||||
@ -34,7 +34,9 @@ export const useWorkflowData = () => {
|
|||||||
// 加载工作流定义详情
|
// 加载工作流定义详情
|
||||||
const loadDefinitionDetail = useCallback(async (graphInstance: Graph, definitionId: string) => {
|
const loadDefinitionDetail = useCallback(async (graphInstance: Graph, definitionId: string) => {
|
||||||
try {
|
try {
|
||||||
|
console.log('正在加载工作流定义详情, ID:', definitionId);
|
||||||
const response = await getDefinitionDetail(Number(definitionId));
|
const response = await getDefinitionDetail(Number(definitionId));
|
||||||
|
console.log('工作流定义详情加载成功:', response);
|
||||||
setTitle(`工作流设计 - ${response.name}`);
|
setTitle(`工作流设计 - ${response.name}`);
|
||||||
setDefinitionData(response);
|
setDefinitionData(response);
|
||||||
|
|
||||||
@ -142,52 +144,18 @@ export const useWorkflowData = () => {
|
|||||||
}
|
}
|
||||||
}, [nodeDefinitions]);
|
}, [nodeDefinitions]);
|
||||||
|
|
||||||
// 合并 LocalVariables Schemas
|
|
||||||
const mergeLocalVariablesSchemas = (schemas: any[]) => {
|
|
||||||
const mergedSchema = {
|
|
||||||
type: 'object',
|
|
||||||
properties: {},
|
|
||||||
required: []
|
|
||||||
};
|
|
||||||
|
|
||||||
schemas.forEach(schema => {
|
|
||||||
if (!schema) return;
|
|
||||||
|
|
||||||
// 合并 properties
|
|
||||||
if (schema.properties) {
|
|
||||||
Object.entries(schema.properties).forEach(([key, property]) => {
|
|
||||||
if ((mergedSchema.properties as any)[key]) {
|
|
||||||
if (JSON.stringify((mergedSchema.properties as any)[key]) !== JSON.stringify(property)) {
|
|
||||||
console.warn(`属性 ${key} 在不同节点中定义不一致,使用第一个定义`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
(mergedSchema.properties as any)[key] = property;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 合并 required 字段
|
|
||||||
if (schema.required) {
|
|
||||||
schema.required.forEach((field: string) => {
|
|
||||||
if (!(mergedSchema.required as any).includes(field)) {
|
|
||||||
(mergedSchema.required as any).push(field);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return mergedSchema;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 保存工作流
|
// 保存工作流
|
||||||
const saveWorkflow = useCallback(async (graph: Graph) => {
|
const saveWorkflow = useCallback(async (graph: Graph) => {
|
||||||
if (!graph) {
|
if (!graph) {
|
||||||
console.error('Graph 实例为空');
|
console.error('Graph 实例为空');
|
||||||
|
message.error('图形实例不存在,无法保存');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!definitionData) {
|
if (!definitionData) {
|
||||||
console.error('definitionData 为空');
|
console.error('definitionData 为空 - 工作流定义数据未加载');
|
||||||
|
message.error('工作流定义数据未加载,请刷新页面重试');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,10 +167,9 @@ export const useWorkflowData = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有节点和边的数据
|
// 获取所有节点和边的数据 - 只保存业务数据,不保存UI配置
|
||||||
const nodes = graph.getNodes().map(node => {
|
const nodes = graph.getNodes().map(node => {
|
||||||
const nodeData = node.getData();
|
const nodeData = node.getData();
|
||||||
const nodeDefinition = nodeDefinitions.find(def => def.nodeType === nodeData.nodeType);
|
|
||||||
const position = node.getPosition();
|
const position = node.getPosition();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -210,14 +177,10 @@ export const useWorkflowData = () => {
|
|||||||
nodeCode: nodeData.nodeCode,
|
nodeCode: nodeData.nodeCode,
|
||||||
nodeType: nodeData.nodeType,
|
nodeType: nodeData.nodeType,
|
||||||
nodeName: nodeData.nodeName,
|
nodeName: nodeData.nodeName,
|
||||||
position,
|
position, // 只保存位置信息
|
||||||
uiConfig: {
|
config: nodeData.config || {}, // 节点配置数据
|
||||||
...(nodeDefinition?.uiConfig || {}),
|
inputMapping: nodeData.inputMapping || {}, // 输入映射数据
|
||||||
position
|
outputMapping: nodeData.outputMapping || {} // 输出映射数据
|
||||||
},
|
|
||||||
config: nodeData.config || {},
|
|
||||||
inputMapping: nodeData.inputMapping || {},
|
|
||||||
outputMapping: nodeData.outputMapping || {}
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -238,24 +201,13 @@ export const useWorkflowData = () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 收集并合并所有节点的输入映射Schema (用于全局变量)
|
// 构建保存数据 - 只保存图形结构,不保存Schema配置
|
||||||
const allInputSchemas = nodes
|
|
||||||
.map(node => {
|
|
||||||
const nodeDefinition = nodeDefinitions.find(def => def.nodeType === node.nodeType);
|
|
||||||
// 检查是否为可配置节点并有输入映射Schema
|
|
||||||
return 'inputMappingSchema' in nodeDefinition! ? nodeDefinition.inputMappingSchema : null;
|
|
||||||
})
|
|
||||||
.filter(schema => schema); // 过滤掉空值
|
|
||||||
const mergedLocalSchema = mergeLocalVariablesSchemas(allInputSchemas);
|
|
||||||
|
|
||||||
// 构建保存数据
|
|
||||||
const saveData = {
|
const saveData = {
|
||||||
...definitionData,
|
...definitionData,
|
||||||
graph: {
|
graph: {
|
||||||
nodes,
|
nodes,
|
||||||
edges
|
edges
|
||||||
},
|
}
|
||||||
localVariablesSchema: mergedLocalSchema
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 调用保存接口
|
// 调用保存接口
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { useState, useCallback } from 'react';
|
|||||||
import { Cell, Edge } from '@antv/x6';
|
import { Cell, Edge } from '@antv/x6';
|
||||||
import { message } from 'antd';
|
import { message } from 'antd';
|
||||||
import type { WorkflowNodeDefinition } from '../nodes/types';
|
import type { WorkflowNodeDefinition } from '../nodes/types';
|
||||||
import type { EdgeCondition } from '../types';
|
import type { EdgeCondition } from '../nodes/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 工作流弹窗管理 Hook
|
* 工作流弹窗管理 Hook
|
||||||
|
|||||||
@ -1,17 +1,20 @@
|
|||||||
.workflow-design {
|
.workflow-design {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: calc(100vh - 184px); // 120px + 24px * 2 + 16px
|
height: calc(100vh - 184px);
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #fff;
|
background: #f8fafc;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
padding: 16px;
|
padding: 16px 24px;
|
||||||
border-bottom: 1px solid #f0f0f0;
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
background: #ffffff;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
|
||||||
|
|
||||||
.back-button {
|
.back-button {
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
@ -35,11 +38,13 @@
|
|||||||
.sidebar {
|
.sidebar {
|
||||||
width: 280px;
|
width: 280px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
background: #ffffff;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
border: 1px solid #f0f0f0;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden; // 防止sidebar本身出现滚动条
|
overflow: hidden;
|
||||||
|
|
||||||
:global {
|
:global {
|
||||||
.ant-collapse {
|
.ant-collapse {
|
||||||
@ -69,6 +74,7 @@
|
|||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
cursor: move;
|
cursor: move;
|
||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
|
background: #ffffff;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #f5f5f5;
|
background: #f5f5f5;
|
||||||
@ -88,7 +94,7 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
border: 1px solid #d9d9d9;
|
border: 1px solid #d9d9d9;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: #f5f5f5;
|
background: #ffffff;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
.workflow-canvas {
|
.workflow-canvas {
|
||||||
|
|||||||
@ -64,7 +64,20 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
|
|
||||||
// 加载工作流数据
|
// 加载工作流数据
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
console.log('工作流数据加载检查:', {
|
||||||
|
hasGraph: !!graph,
|
||||||
|
hasId: !!id,
|
||||||
|
id,
|
||||||
|
isNodeDefinitionsLoaded
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
console.error('工作流ID缺失,无法加载定义数据');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (graph && id && isNodeDefinitionsLoaded) {
|
if (graph && id && isNodeDefinitionsLoaded) {
|
||||||
|
console.log('开始加载工作流定义详情:', id);
|
||||||
loadDefinitionDetail(graph, id);
|
loadDefinitionDetail(graph, id);
|
||||||
}
|
}
|
||||||
}, [graph, id, isNodeDefinitionsLoaded, loadDefinitionDetail]);
|
}, [graph, id, isNodeDefinitionsLoaded, loadDefinitionDetail]);
|
||||||
|
|||||||
@ -1,76 +0,0 @@
|
|||||||
/**
|
|
||||||
* 节点数据转换器
|
|
||||||
* 负责在设计时Schema格式和运行时key/value格式之间转换
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {
|
|
||||||
ConfigurableNodeDefinition,
|
|
||||||
NodeInstanceData,
|
|
||||||
NodeFormData,
|
|
||||||
WorkflowNodeDefinition,
|
|
||||||
UIConfig
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
export class NodeDataConverter {
|
|
||||||
/**
|
|
||||||
* 判断节点是否为可配置节点
|
|
||||||
*/
|
|
||||||
static isConfigurableNode(node: WorkflowNodeDefinition): node is ConfigurableNodeDefinition {
|
|
||||||
return 'inputMappingSchema' in node || 'outputMappingSchema' in node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 保存时:将用户表单数据转换为后端存储格式
|
|
||||||
*/
|
|
||||||
static convertToSaveFormat(
|
|
||||||
nodeDefinition: WorkflowNodeDefinition,
|
|
||||||
formData: NodeFormData,
|
|
||||||
position: { x: number; y: number },
|
|
||||||
uiConfig: UIConfig
|
|
||||||
): NodeInstanceData {
|
|
||||||
const baseData = {
|
|
||||||
nodeCode: nodeDefinition.nodeCode,
|
|
||||||
nodeName: nodeDefinition.nodeName,
|
|
||||||
nodeType: nodeDefinition.nodeType,
|
|
||||||
category: nodeDefinition.category,
|
|
||||||
description: nodeDefinition.description,
|
|
||||||
position,
|
|
||||||
uiConfig,
|
|
||||||
config: formData.config || {},
|
|
||||||
};
|
|
||||||
|
|
||||||
// 如果是可配置节点,添加输入输出映射
|
|
||||||
if (this.isConfigurableNode(nodeDefinition)) {
|
|
||||||
return {
|
|
||||||
...baseData,
|
|
||||||
inputMapping: formData.inputMapping || {},
|
|
||||||
outputMapping: formData.outputMapping || {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return baseData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 加载时:将后端数据转换为表单格式
|
|
||||||
*/
|
|
||||||
static convertToFormFormat(
|
|
||||||
nodeDefinition: WorkflowNodeDefinition,
|
|
||||||
savedData: NodeInstanceData
|
|
||||||
): NodeFormData {
|
|
||||||
const formData: NodeFormData = {
|
|
||||||
config: savedData.config || {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 如果是可配置节点,添加输入输出映射
|
|
||||||
if (this.isConfigurableNode(nodeDefinition)) {
|
|
||||||
return {
|
|
||||||
...formData,
|
|
||||||
inputMapping: savedData.inputMapping || {},
|
|
||||||
outputMapping: savedData.outputMapping || {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return formData;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -11,12 +11,12 @@ export const DeployNode: ConfigurableNodeDefinition = {
|
|||||||
category: NodeCategory.TASK,
|
category: NodeCategory.TASK,
|
||||||
description: "执行应用构建和部署任务",
|
description: "执行应用构建和部署任务",
|
||||||
|
|
||||||
// UI 配置 - 节点在画布上的显示样式
|
// UI 配置 - 节点在画布上的显示样式(现代化设计)
|
||||||
uiConfig: {
|
uiConfig: {
|
||||||
shape: 'rect',
|
shape: 'rect',
|
||||||
size: {
|
size: {
|
||||||
width: 120,
|
width: 160,
|
||||||
height: 60
|
height: 80
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
fill: '#1890ff',
|
fill: '#1890ff',
|
||||||
@ -27,23 +27,43 @@ export const DeployNode: ConfigurableNodeDefinition = {
|
|||||||
},
|
},
|
||||||
ports: {
|
ports: {
|
||||||
groups: {
|
groups: {
|
||||||
|
// 输入端口 - 现代化样式
|
||||||
in: {
|
in: {
|
||||||
position: 'left',
|
position: 'left',
|
||||||
attrs: {
|
attrs: {
|
||||||
circle: {
|
circle: {
|
||||||
r: 4,
|
r: 7,
|
||||||
fill: '#fff',
|
fill: '#ffffff',
|
||||||
stroke: '#1890ff'
|
stroke: '#3b82f6',
|
||||||
|
strokeWidth: 2.5,
|
||||||
|
// 现代化端口样式
|
||||||
|
filter: 'drop-shadow(0 2px 4px rgba(59, 130, 246, 0.3))',
|
||||||
|
// 端口悬浮效果
|
||||||
|
':hover': {
|
||||||
|
r: 8,
|
||||||
|
fill: '#dbeafe',
|
||||||
|
stroke: '#2563eb'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 输出端口 - 现代化样式
|
||||||
out: {
|
out: {
|
||||||
position: 'right',
|
position: 'right',
|
||||||
attrs: {
|
attrs: {
|
||||||
circle: {
|
circle: {
|
||||||
r: 4,
|
r: 7,
|
||||||
fill: '#fff',
|
fill: '#ffffff',
|
||||||
stroke: '#1890ff'
|
stroke: '#3b82f6',
|
||||||
|
strokeWidth: 2.5,
|
||||||
|
// 现代化端口样式
|
||||||
|
filter: 'drop-shadow(0 2px 4px rgba(59, 130, 246, 0.3))',
|
||||||
|
// 端口悬浮效果
|
||||||
|
':hover': {
|
||||||
|
r: 8,
|
||||||
|
fill: '#dbeafe',
|
||||||
|
stroke: '#2563eb'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,17 +81,20 @@ export const DeployNode: ConfigurableNodeDefinition = {
|
|||||||
nodeName: {
|
nodeName: {
|
||||||
type: "string",
|
type: "string",
|
||||||
title: "节点名称",
|
title: "节点名称",
|
||||||
description: "节点在流程图中显示的名称"
|
description: "节点在流程图中显示的名称",
|
||||||
|
default: "构建任务"
|
||||||
},
|
},
|
||||||
nodeCode: {
|
nodeCode: {
|
||||||
type: "string",
|
type: "string",
|
||||||
title: "节点编码",
|
title: "节点编码",
|
||||||
description: "节点的唯一标识符"
|
description: "节点的唯一标识符",
|
||||||
|
default: "DEPLOY_NODE"
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: "string",
|
type: "string",
|
||||||
title: "节点描述",
|
title: "节点描述",
|
||||||
description: "节点的详细说明"
|
description: "节点的详细说明",
|
||||||
|
default: "执行应用构建和部署任务"
|
||||||
},
|
},
|
||||||
// 节点配置
|
// 节点配置
|
||||||
buildCommand: {
|
buildCommand: {
|
||||||
|
|||||||
@ -11,23 +11,23 @@ export const EndEventNode: BaseNodeDefinition = {
|
|||||||
category: NodeCategory.EVENT,
|
category: NodeCategory.EVENT,
|
||||||
description: "工作流的结束节点",
|
description: "工作流的结束节点",
|
||||||
|
|
||||||
// UI 配置 - 节点在画布上的显示样式
|
// UI 配置 - 节点在画布上的显示样式(现代化设计)
|
||||||
uiConfig: {
|
uiConfig: {
|
||||||
shape: 'circle',
|
shape: 'circle',
|
||||||
size: {
|
size: {
|
||||||
width: 40,
|
width: 60,
|
||||||
height: 40
|
height: 60
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
fill: '#f5222d',
|
fill: '#f5222d',
|
||||||
stroke: '#cf1322',
|
stroke: '#cf1322',
|
||||||
strokeWidth: 1,
|
strokeWidth: 2,
|
||||||
icon: 'stop',
|
icon: 'stop',
|
||||||
iconColor: '#fff'
|
iconColor: '#fff'
|
||||||
},
|
},
|
||||||
ports: {
|
ports: {
|
||||||
groups: {
|
groups: {
|
||||||
// 结束节点只有输入端口
|
// 结束节点只有输入端口 - 现代化样式
|
||||||
in: {
|
in: {
|
||||||
position: 'left',
|
position: 'left',
|
||||||
attrs: {
|
attrs: {
|
||||||
@ -51,17 +51,20 @@ export const EndEventNode: BaseNodeDefinition = {
|
|||||||
nodeName: {
|
nodeName: {
|
||||||
type: "string",
|
type: "string",
|
||||||
title: "节点名称",
|
title: "节点名称",
|
||||||
description: "节点在流程图中显示的名称"
|
description: "节点在流程图中显示的名称",
|
||||||
|
default: "结束"
|
||||||
},
|
},
|
||||||
nodeCode: {
|
nodeCode: {
|
||||||
type: "string",
|
type: "string",
|
||||||
title: "节点编码",
|
title: "节点编码",
|
||||||
description: "节点的唯一标识符"
|
description: "节点的唯一标识符",
|
||||||
|
default: "END_EVENT"
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
type: "string",
|
type: "string",
|
||||||
title: "节点描述",
|
title: "节点描述",
|
||||||
description: "节点的详细说明"
|
description: "节点的详细说明",
|
||||||
|
default: "工作流的结束节点"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
required: ["nodeName", "nodeCode"]
|
required: ["nodeName", "nodeCode"]
|
||||||
|
|||||||
@ -1,33 +1,33 @@
|
|||||||
import { BaseNodeDefinition, NodeType, NodeCategory } from '../types';
|
import {BaseNodeDefinition, NodeType, NodeCategory} from '../types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 开始事件节点定义
|
* 开始事件节点定义
|
||||||
* 简单节点,无需额外配置
|
* 简单节点,无需额外配置
|
||||||
*/
|
*/
|
||||||
export const StartEventNode: BaseNodeDefinition = {
|
export const StartEventNode: BaseNodeDefinition = {
|
||||||
nodeCode: "START_EVENT",
|
nodeCode: "START_EVENT",
|
||||||
nodeName: "开始",
|
nodeName: "开始",
|
||||||
nodeType: NodeType.START_EVENT,
|
nodeType: NodeType.START_EVENT,
|
||||||
category: NodeCategory.EVENT,
|
category: NodeCategory.EVENT,
|
||||||
description: "工作流的起始节点",
|
description: "工作流的起始节点",
|
||||||
|
|
||||||
// UI 配置 - 节点在画布上的显示样式
|
// UI 配置 - 节点在画布上的显示样式(现代化设计)
|
||||||
uiConfig: {
|
uiConfig: {
|
||||||
shape: 'circle',
|
shape: 'circle',
|
||||||
size: {
|
size: {
|
||||||
width: 40,
|
width: 60,
|
||||||
height: 40
|
height: 60
|
||||||
},
|
},
|
||||||
style: {
|
style: {
|
||||||
fill: '#52c41a',
|
fill: '#52c41a',
|
||||||
stroke: '#389e08',
|
stroke: '#389e08',
|
||||||
strokeWidth: 1,
|
strokeWidth: 2,
|
||||||
icon: 'play-circle',
|
icon: 'play-circle',
|
||||||
iconColor: '#fff'
|
iconColor: '#fff'
|
||||||
},
|
},
|
||||||
ports: {
|
ports: {
|
||||||
groups: {
|
groups: {
|
||||||
// 开始节点只有输出端口
|
// 开始节点只有输出端口 - 现代化样式
|
||||||
out: {
|
out: {
|
||||||
position: 'right',
|
position: 'right',
|
||||||
attrs: {
|
attrs: {
|
||||||
@ -42,28 +42,31 @@ export const StartEventNode: BaseNodeDefinition = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 基本配置Schema - 用于生成"基本配置"TAB(包含基本信息)
|
// 基本配置Schema - 用于生成"基本配置"TAB(包含基本信息)
|
||||||
configSchema: {
|
configSchema: {
|
||||||
type: "object",
|
type: "object",
|
||||||
title: "基本配置",
|
title: "基本配置",
|
||||||
description: "节点的基本配置信息",
|
description: "节点的基本配置信息",
|
||||||
properties: {
|
properties: {
|
||||||
nodeName: {
|
nodeName: {
|
||||||
type: "string",
|
type: "string",
|
||||||
title: "节点名称",
|
title: "节点名称",
|
||||||
description: "节点在流程图中显示的名称"
|
description: "节点在流程图中显示的名称",
|
||||||
},
|
default: "开始"
|
||||||
nodeCode: {
|
},
|
||||||
type: "string",
|
nodeCode: {
|
||||||
title: "节点编码",
|
type: "string",
|
||||||
description: "节点的唯一标识符"
|
title: "节点编码",
|
||||||
},
|
description: "节点的唯一标识符",
|
||||||
description: {
|
default: "START_EVENT"
|
||||||
type: "string",
|
},
|
||||||
title: "节点描述",
|
description: {
|
||||||
description: "节点的详细说明"
|
type: "string",
|
||||||
}
|
title: "节点描述",
|
||||||
},
|
description: "节点的详细说明",
|
||||||
required: ["nodeName", "nodeCode"]
|
default: "工作流的起始节点"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
required: ["nodeName", "nodeCode"]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
@ -1,76 +1,23 @@
|
|||||||
import { WorkflowNodeDefinition, NodeCategory, NodeType } from '../types';
|
import {WorkflowNodeDefinition, ConfigurableNodeDefinition, NodeCategory, NodeType} from '../types';
|
||||||
import { NodeDataConverter } from '../NodeDataConverter';
|
import {DeployNode} from './DeployNode';
|
||||||
import { DeployNode } from './DeployNode';
|
import {StartEventNode} from './StartEventNode';
|
||||||
import { StartEventNode } from './StartEventNode';
|
import {EndEventNode} from './EndEventNode';
|
||||||
import { EndEventNode } from './EndEventNode';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 所有节点定义的注册表
|
* 所有节点定义的注册表
|
||||||
*/
|
*/
|
||||||
export const NODE_DEFINITIONS: WorkflowNodeDefinition[] = [
|
export const NODE_DEFINITIONS: WorkflowNodeDefinition[] = [
|
||||||
StartEventNode,
|
StartEventNode,
|
||||||
EndEventNode,
|
EndEventNode,
|
||||||
DeployNode,
|
DeployNode,
|
||||||
// 在这里添加更多节点定义
|
// 在这里添加更多节点定义
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据节点类型获取节点定义
|
|
||||||
*/
|
|
||||||
export const getNodeDefinition = (nodeType: NodeType): WorkflowNodeDefinition | undefined => {
|
|
||||||
return NODE_DEFINITIONS.find(node => node.nodeType === nodeType);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据节点代码获取节点定义
|
|
||||||
*/
|
|
||||||
export const getNodeDefinitionByCode = (nodeCode: string): WorkflowNodeDefinition | undefined => {
|
|
||||||
return NODE_DEFINITIONS.find(node => node.nodeCode === nodeCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取指定分类的所有节点
|
|
||||||
*/
|
|
||||||
export const getNodesByCategory = (category: NodeCategory): WorkflowNodeDefinition[] => {
|
|
||||||
return NODE_DEFINITIONS.filter(node => node.category === category);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取按分类分组的节点定义
|
|
||||||
*/
|
|
||||||
export const getNodesByCategories = (): Record<NodeCategory, WorkflowNodeDefinition[]> => {
|
|
||||||
return NODE_DEFINITIONS.reduce((acc, node) => {
|
|
||||||
if (!acc[node.category]) {
|
|
||||||
acc[node.category] = [];
|
|
||||||
}
|
|
||||||
acc[node.category].push(node);
|
|
||||||
return acc;
|
|
||||||
}, {} as Record<NodeCategory, WorkflowNodeDefinition[]>);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有已启用的节点定义
|
|
||||||
*/
|
|
||||||
export const getEnabledNodes = (): WorkflowNodeDefinition[] => {
|
|
||||||
// 目前所有节点都是启用的,后续可以添加enabled字段
|
|
||||||
return NODE_DEFINITIONS;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 检查节点是否为可配置节点
|
|
||||||
*/
|
|
||||||
export const isConfigurableNode = NodeDataConverter.isConfigurableNode;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 导出节点定义
|
* 导出节点定义
|
||||||
*/
|
*/
|
||||||
export {
|
export {
|
||||||
StartEventNode,
|
StartEventNode,
|
||||||
EndEventNode,
|
EndEventNode,
|
||||||
DeployNode
|
DeployNode
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出数据转换器
|
|
||||||
*/
|
|
||||||
export { NodeDataConverter };
|
|
||||||
@ -2,58 +2,16 @@
|
|||||||
* 节点服务 - 提供节点定义的访问接口
|
* 节点服务 - 提供节点定义的访问接口
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {NODE_DEFINITIONS} from './definitions';
|
||||||
NODE_DEFINITIONS,
|
import {WorkflowNodeDefinition} from './types';
|
||||||
getNodeDefinition,
|
|
||||||
getNodesByCategory,
|
|
||||||
getNodesByCategories
|
|
||||||
} from './definitions';
|
|
||||||
import {
|
|
||||||
WorkflowNodeDefinition,
|
|
||||||
NodeType,
|
|
||||||
NodeCategory
|
|
||||||
} from './types';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有节点定义列表
|
* 获取所有节点定义列表
|
||||||
*/
|
*/
|
||||||
export const getNodeDefinitionList = async (): Promise<WorkflowNodeDefinition[]> => {
|
export const getNodeDefinitionList = async (): Promise<WorkflowNodeDefinition[]> => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
resolve(NODE_DEFINITIONS);
|
resolve(NODE_DEFINITIONS);
|
||||||
}, 10);
|
}, 10);
|
||||||
});
|
});
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取节点定义列表,按分类分组
|
|
||||||
*/
|
|
||||||
export const getNodeDefinitionsGroupedByCategory = async (): Promise<Record<NodeCategory, WorkflowNodeDefinition[]>> => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve(getNodesByCategories());
|
|
||||||
}, 10);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据节点类型获取节点定义
|
|
||||||
*/
|
|
||||||
export const getNodeDefinitionByType = async (nodeType: NodeType): Promise<WorkflowNodeDefinition | undefined> => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve(getNodeDefinition(nodeType));
|
|
||||||
}, 10);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据分类获取节点定义
|
|
||||||
*/
|
|
||||||
export const getNodeDefinitionsByCategory = async (category: NodeCategory): Promise<WorkflowNodeDefinition[]> => {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
resolve(getNodesByCategory(category));
|
|
||||||
}, 10);
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
@ -1,4 +1,22 @@
|
|||||||
import { BaseResponse } from "@/types/base";
|
|
||||||
|
// 节点分类 (从 ../types.ts 合并)
|
||||||
|
export enum NodeCategory {
|
||||||
|
EVENT = 'EVENT',
|
||||||
|
TASK = 'TASK',
|
||||||
|
GATEWAY = 'GATEWAY',
|
||||||
|
CONTAINER = 'CONTAINER'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 条件类型
|
||||||
|
export type ConditionType = 'EXPRESSION' | 'SCRIPT' | 'DEFAULT';
|
||||||
|
|
||||||
|
// 边的条件配置
|
||||||
|
export interface EdgeCondition {
|
||||||
|
type: ConditionType;
|
||||||
|
expression?: string;
|
||||||
|
script?: string;
|
||||||
|
priority: number;
|
||||||
|
}
|
||||||
|
|
||||||
// JSON Schema 定义
|
// JSON Schema 定义
|
||||||
export interface JSONSchema {
|
export interface JSONSchema {
|
||||||
@ -19,6 +37,13 @@ export interface PortStyle {
|
|||||||
r: number;
|
r: number;
|
||||||
fill: string;
|
fill: string;
|
||||||
stroke: string;
|
stroke: string;
|
||||||
|
strokeWidth?: number; // 新增:端口描边宽度
|
||||||
|
filter?: string; // 新增:端口滤镜效果
|
||||||
|
':hover'?: { // 新增:端口悬浮状态
|
||||||
|
r?: number;
|
||||||
|
fill?: string;
|
||||||
|
stroke?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface PortAttributes {
|
export interface PortAttributes {
|
||||||
@ -45,6 +70,24 @@ export interface NodeStyle {
|
|||||||
stroke: string;
|
stroke: string;
|
||||||
iconColor: string;
|
iconColor: string;
|
||||||
strokeWidth: number;
|
strokeWidth: number;
|
||||||
|
// 现代化样式属性
|
||||||
|
iconSize?: number; // 新增:图标大小
|
||||||
|
borderRadius?: string; // 新增:圆角
|
||||||
|
boxShadow?: string; // 新增:阴影
|
||||||
|
transition?: string; // 新增:过渡效果
|
||||||
|
fontSize?: string; // 新增:字体大小
|
||||||
|
fontWeight?: string; // 新增:字体粗细
|
||||||
|
fontFamily?: string; // 新增:字体族
|
||||||
|
':hover'?: { // 新增:悬浮状态
|
||||||
|
fill?: string;
|
||||||
|
stroke?: string;
|
||||||
|
boxShadow?: string;
|
||||||
|
transform?: string;
|
||||||
|
};
|
||||||
|
':active'?: { // 新增:激活状态
|
||||||
|
transform?: string;
|
||||||
|
boxShadow?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UIConfig {
|
export interface UIConfig {
|
||||||
@ -59,23 +102,17 @@ export interface UIConfig {
|
|||||||
|
|
||||||
// 节点类型和分类(保持原有的枚举格式)
|
// 节点类型和分类(保持原有的枚举格式)
|
||||||
export enum NodeType {
|
export enum NodeType {
|
||||||
START_EVENT = 'START_EVENT',
|
START_EVENT = 'START_EVENT',
|
||||||
END_EVENT = 'END_EVENT',
|
END_EVENT = 'END_EVENT',
|
||||||
USER_TASK = 'USER_TASK',
|
USER_TASK = 'USER_TASK',
|
||||||
SERVICE_TASK = 'SERVICE_TASK',
|
SERVICE_TASK = 'SERVICE_TASK',
|
||||||
SCRIPT_TASK = 'SCRIPT_TASK',
|
SCRIPT_TASK = 'SCRIPT_TASK',
|
||||||
DEPLOY_NODE = 'DEPLOY_NODE',
|
DEPLOY_NODE = 'DEPLOY_NODE',
|
||||||
GATEWAY_NODE = 'GATEWAY_NODE',
|
GATEWAY_NODE = 'GATEWAY_NODE',
|
||||||
SUB_PROCESS = 'SUB_PROCESS',
|
SUB_PROCESS = 'SUB_PROCESS',
|
||||||
CALL_ACTIVITY = 'CALL_ACTIVITY'
|
CALL_ACTIVITY = 'CALL_ACTIVITY'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NodeCategory {
|
|
||||||
EVENT = 'EVENT',
|
|
||||||
TASK = 'TASK',
|
|
||||||
GATEWAY = 'GATEWAY',
|
|
||||||
CONTAINER = 'CONTAINER'
|
|
||||||
}
|
|
||||||
|
|
||||||
// 基础节点定义(只有基本配置)
|
// 基础节点定义(只有基本配置)
|
||||||
export interface BaseNodeDefinition {
|
export interface BaseNodeDefinition {
|
||||||
@ -106,7 +143,7 @@ export interface NodeInstanceData {
|
|||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
// 运行时数据(key/value格式)
|
// 运行时数据(key/value格式)
|
||||||
config?: Record<string, any>; // 基本配置数据(包含基本信息+节点配置)
|
configs?: Record<string, any>; // 基本配置数据(包含基本信息+节点配置)
|
||||||
inputMapping?: Record<string, any>;
|
inputMapping?: Record<string, any>;
|
||||||
outputMapping?: Record<string, any>;
|
outputMapping?: Record<string, any>;
|
||||||
|
|
||||||
@ -114,27 +151,3 @@ export interface NodeInstanceData {
|
|||||||
position: { x: number; y: number };
|
position: { x: number; y: number };
|
||||||
uiConfig: UIConfig; // 包含运行时可能更新的UI配置,如位置
|
uiConfig: UIConfig; // 包含运行时可能更新的UI配置,如位置
|
||||||
}
|
}
|
||||||
|
|
||||||
// 兼容现有API的响应格式
|
|
||||||
export interface NodeDefinitionResponse extends BaseResponse {
|
|
||||||
nodeCode: string;
|
|
||||||
nodeName: string;
|
|
||||||
nodeType: NodeType;
|
|
||||||
category: NodeCategory;
|
|
||||||
description: string;
|
|
||||||
uiConfig: UIConfig | null;
|
|
||||||
configSchema?: JSONSchema | null; // 基本配置Schema
|
|
||||||
|
|
||||||
// 兼容字段(保持现有组件正常工作)
|
|
||||||
panelVariablesSchema?: JSONSchema | null;
|
|
||||||
localVariablesSchema?: JSONSchema | null;
|
|
||||||
panelVariables?: Record<string, any>;
|
|
||||||
localVariables?: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据转换器工具类型
|
|
||||||
export interface NodeFormData {
|
|
||||||
config?: Record<string, any>; // 基本配置表单数据(包含基本信息+节点配置)
|
|
||||||
inputMapping?: Record<string, any>;
|
|
||||||
outputMapping?: Record<string, any>;
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
// 节点分类
|
|
||||||
export type NodeCategory = 'EVENT' | 'TASK' | 'GATEWAY' | 'CONTAINER';
|
|
||||||
|
|
||||||
// 条件类型
|
|
||||||
export type ConditionType = 'EXPRESSION' | 'SCRIPT' | 'DEFAULT';
|
|
||||||
|
|
||||||
// 边的条件配置
|
|
||||||
export interface EdgeCondition {
|
|
||||||
type: ConditionType;
|
|
||||||
expression?: string;
|
|
||||||
script?: string;
|
|
||||||
priority: number;
|
|
||||||
}
|
|
||||||
@ -44,7 +44,7 @@ export class EventRegistrar {
|
|||||||
PortManager.showPorts(node.id);
|
PortManager.showPorts(node.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.graph.on('node:mouseleave', ({node}: any) => {
|
this.graph.on('node:mouseleave', ({node}) => {
|
||||||
NodeStyleManager.resetNodeStyle(node);
|
NodeStyleManager.resetNodeStyle(node);
|
||||||
PortManager.hidePorts(node.id);
|
PortManager.hidePorts(node.id);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,135 +1,136 @@
|
|||||||
import { Graph } from '@antv/x6';
|
import {Graph} from '@antv/x6';
|
||||||
import { WorkflowNodeDefinition, NodeInstanceData } from '../nodes/types';
|
import {WorkflowNodeDefinition, NodeInstanceData} from '../nodes/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从节点定义创建新节点
|
* 从节点定义创建新节点
|
||||||
*/
|
*/
|
||||||
export const createNodeFromDefinition = (
|
export const createNodeFromDefinition = (
|
||||||
graph: Graph,
|
graph: Graph,
|
||||||
nodeDefinition: WorkflowNodeDefinition,
|
nodeDefinition: WorkflowNodeDefinition,
|
||||||
position: { x: number; y: number }
|
position: { x: number; y: number }
|
||||||
) => {
|
) => {
|
||||||
const { uiConfig } = nodeDefinition;
|
const {uiConfig} = nodeDefinition;
|
||||||
|
|
||||||
// 根据形状类型设置正确的 shape
|
// 根据形状类型设置正确的 shape
|
||||||
let shape = 'rect';
|
let shape = 'rect';
|
||||||
if (uiConfig.shape === 'circle') {
|
if (uiConfig.shape === 'circle') {
|
||||||
shape = 'circle';
|
shape = 'circle';
|
||||||
} else if (uiConfig.shape === 'diamond') {
|
} else if (uiConfig.shape === 'diamond') {
|
||||||
shape = 'polygon';
|
shape = 'polygon';
|
||||||
}
|
|
||||||
|
|
||||||
// 创建节点配置
|
|
||||||
const nodeConfig = {
|
|
||||||
shape,
|
|
||||||
x: position.x,
|
|
||||||
y: position.y,
|
|
||||||
width: uiConfig.size.width,
|
|
||||||
height: uiConfig.size.height,
|
|
||||||
attrs: {
|
|
||||||
body: {
|
|
||||||
...uiConfig.style,
|
|
||||||
...(uiConfig.shape === 'diamond' ? {
|
|
||||||
refPoints: '0,10 10,0 20,10 10,20',
|
|
||||||
} : {})
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
text: nodeDefinition.nodeName,
|
|
||||||
fontSize: 12,
|
|
||||||
fill: '#000'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ports: convertPortConfig(uiConfig.ports),
|
|
||||||
// 同时设置为props和data,方便访问
|
|
||||||
nodeType: nodeDefinition.nodeType,
|
|
||||||
nodeCode: nodeDefinition.nodeCode,
|
|
||||||
data: {
|
|
||||||
nodeType: nodeDefinition.nodeType,
|
|
||||||
nodeCode: nodeDefinition.nodeCode,
|
|
||||||
nodeName: nodeDefinition.nodeName
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return graph.addNode(nodeConfig);
|
// 创建节点配置
|
||||||
|
const nodeConfig = {
|
||||||
|
shape,
|
||||||
|
x: position.x,
|
||||||
|
y: position.y,
|
||||||
|
width: uiConfig.size.width,
|
||||||
|
height: uiConfig.size.height,
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
...uiConfig.style,
|
||||||
|
...(uiConfig.shape === 'diamond' ? {
|
||||||
|
refPoints: '0,10 10,0 20,10 10,20',
|
||||||
|
} : {})
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
text: nodeDefinition.nodeName,
|
||||||
|
fontSize: 12,
|
||||||
|
fill: '#000'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ports: convertPortConfig(uiConfig.ports),
|
||||||
|
// 同时设置为props和data,方便访问
|
||||||
|
nodeType: nodeDefinition.nodeType,
|
||||||
|
nodeCode: nodeDefinition.nodeCode,
|
||||||
|
data: {
|
||||||
|
nodeType: nodeDefinition.nodeType,
|
||||||
|
nodeCode: nodeDefinition.nodeCode,
|
||||||
|
nodeName: nodeDefinition.nodeName
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return graph.addNode(nodeConfig);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 从保存的数据恢复节点
|
* 从保存的数据恢复节点
|
||||||
*/
|
*/
|
||||||
export const restoreNodeFromData = (
|
export const restoreNodeFromData = (
|
||||||
graph: Graph,
|
graph: Graph,
|
||||||
nodeData: NodeInstanceData,
|
nodeData: NodeInstanceData,
|
||||||
_nodeDefinition: WorkflowNodeDefinition
|
nodeDefinition: WorkflowNodeDefinition
|
||||||
) => {
|
) => {
|
||||||
const { uiConfig } = nodeData;
|
// 从节点定义中获取UI配置,兼容旧数据中的uiConfig
|
||||||
|
const uiConfig = nodeData.uiConfig || nodeDefinition.uiConfig;
|
||||||
|
|
||||||
// 根据形状类型设置正确的 shape
|
// 根据形状类型设置正确的 shape
|
||||||
let shape = 'rect';
|
let shape = 'rect';
|
||||||
if (uiConfig.shape === 'circle') {
|
if (uiConfig.shape === 'circle') {
|
||||||
shape = 'circle';
|
shape = 'circle';
|
||||||
} else if (uiConfig.shape === 'diamond') {
|
} else if (uiConfig.shape === 'diamond') {
|
||||||
shape = 'polygon';
|
shape = 'polygon';
|
||||||
}
|
|
||||||
|
|
||||||
// 创建节点配置
|
|
||||||
const nodeConfig = {
|
|
||||||
id: nodeData.nodeCode, // 使用保存的ID
|
|
||||||
shape,
|
|
||||||
x: nodeData.position.x,
|
|
||||||
y: nodeData.position.y,
|
|
||||||
width: uiConfig.size.width,
|
|
||||||
height: uiConfig.size.height,
|
|
||||||
attrs: {
|
|
||||||
body: {
|
|
||||||
...uiConfig.style,
|
|
||||||
...(uiConfig.shape === 'diamond' ? {
|
|
||||||
refPoints: '0,10 10,0 20,10 10,20',
|
|
||||||
} : {})
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
text: nodeData.nodeName,
|
|
||||||
fontSize: 12,
|
|
||||||
fill: '#000'
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ports: convertPortConfig(uiConfig.ports),
|
|
||||||
// 同时设置为props和data,方便访问
|
|
||||||
nodeType: nodeData.nodeType,
|
|
||||||
nodeCode: nodeData.nodeCode,
|
|
||||||
data: {
|
|
||||||
nodeType: nodeData.nodeType,
|
|
||||||
nodeCode: nodeData.nodeCode,
|
|
||||||
nodeName: nodeData.nodeName,
|
|
||||||
config: nodeData.config,
|
|
||||||
inputMapping: nodeData.inputMapping,
|
|
||||||
outputMapping: nodeData.outputMapping
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
return graph.addNode(nodeConfig);
|
// 创建节点配置
|
||||||
|
const nodeConfig = {
|
||||||
|
id: nodeData.nodeCode, // 使用保存的ID
|
||||||
|
shape,
|
||||||
|
x: nodeData.position.x,
|
||||||
|
y: nodeData.position.y,
|
||||||
|
width: uiConfig.size.width,
|
||||||
|
height: uiConfig.size.height,
|
||||||
|
attrs: {
|
||||||
|
body: {
|
||||||
|
...uiConfig.style,
|
||||||
|
...(uiConfig.shape === 'diamond' ? {
|
||||||
|
refPoints: '0,10 10,0 20,10 10,20',
|
||||||
|
} : {})
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
text: nodeData.nodeName,
|
||||||
|
fontSize: 12,
|
||||||
|
fill: '#000'
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ports: convertPortConfig(uiConfig.ports),
|
||||||
|
// 同时设置为props和data,方便访问
|
||||||
|
nodeType: nodeData.nodeType,
|
||||||
|
nodeCode: nodeData.nodeCode,
|
||||||
|
data: {
|
||||||
|
nodeType: nodeData.nodeType,
|
||||||
|
nodeCode: nodeData.nodeCode,
|
||||||
|
nodeName: nodeData.nodeName,
|
||||||
|
configs: nodeData.configs,
|
||||||
|
inputMapping: nodeData.inputMapping,
|
||||||
|
outputMapping: nodeData.outputMapping
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return graph.addNode(nodeConfig);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 转换端口配置为X6格式
|
* 转换端口配置为X6格式
|
||||||
*/
|
*/
|
||||||
const convertPortConfig = (ports: any) => {
|
const convertPortConfig = (ports: any) => {
|
||||||
if (!ports?.groups) return { items: [] };
|
if (!ports?.groups) return {items: []};
|
||||||
|
|
||||||
const groups: any = {};
|
const groups: any = {};
|
||||||
const items: any[] = [];
|
const items: any[] = [];
|
||||||
|
|
||||||
Object.entries(ports.groups).forEach(([key, group]: [string, any]) => {
|
Object.entries(ports.groups).forEach(([key, group]: [string, any]) => {
|
||||||
groups[key] = {
|
groups[key] = {
|
||||||
position: group.position,
|
position: group.position,
|
||||||
attrs: {
|
attrs: {
|
||||||
circle: {
|
circle: {
|
||||||
...group.attrs.circle,
|
...group.attrs.circle,
|
||||||
magnet: true,
|
magnet: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
items.push({ group: key });
|
items.push({group: key});
|
||||||
});
|
});
|
||||||
|
|
||||||
return { groups, items };
|
return {groups, items};
|
||||||
};
|
};
|
||||||
Loading…
Reference in New Issue
Block a user