1
This commit is contained in:
parent
3543906817
commit
d7c6af621b
@ -0,0 +1,93 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Modal, Form, Input, InputNumber, Radio } from 'antd';
|
||||||
|
import { Edge } from '@antv/x6';
|
||||||
|
import { ConditionType, EdgeCondition } from '../types';
|
||||||
|
|
||||||
|
interface ExpressionModalProps {
|
||||||
|
visible: boolean;
|
||||||
|
edge: Edge;
|
||||||
|
onOk: (condition: EdgeCondition) => void;
|
||||||
|
onCancel: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ExpressionModal: React.FC<ExpressionModalProps> = ({
|
||||||
|
visible,
|
||||||
|
edge,
|
||||||
|
onOk,
|
||||||
|
onCancel
|
||||||
|
}) => {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const currentCondition = edge.getProp('condition') as EdgeCondition;
|
||||||
|
|
||||||
|
const handleOk = async () => {
|
||||||
|
try {
|
||||||
|
const values = await form.validateFields();
|
||||||
|
onOk(values);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('表单验证失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="编辑条件"
|
||||||
|
open={visible}
|
||||||
|
onOk={handleOk}
|
||||||
|
onCancel={onCancel}
|
||||||
|
width={600}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={form}
|
||||||
|
layout="vertical"
|
||||||
|
initialValues={currentCondition || {
|
||||||
|
type: 'EXPRESSION',
|
||||||
|
priority: 10
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Form.Item
|
||||||
|
name="type"
|
||||||
|
label="条件类型"
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
>
|
||||||
|
<Radio.Group>
|
||||||
|
<Radio value="EXPRESSION">表达式</Radio>
|
||||||
|
<Radio value="DEFAULT">默认路径</Radio>
|
||||||
|
</Radio.Group>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
noStyle
|
||||||
|
shouldUpdate={(prevValues, currentValues) => prevValues.type !== currentValues.type}
|
||||||
|
>
|
||||||
|
{({ getFieldValue }) => {
|
||||||
|
const type = getFieldValue('type');
|
||||||
|
return type === 'EXPRESSION' ? (
|
||||||
|
<Form.Item
|
||||||
|
name="expression"
|
||||||
|
label="条件表达式"
|
||||||
|
rules={[{ required: true, message: '请输入条件表达式' }]}
|
||||||
|
extra="支持使用 ${变量名} 引用流程变量,例如:${amount} > 1000"
|
||||||
|
>
|
||||||
|
<Input.TextArea
|
||||||
|
placeholder="请输入条件表达式"
|
||||||
|
rows={4}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
) : null;
|
||||||
|
}}
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item
|
||||||
|
name="priority"
|
||||||
|
label="优先级"
|
||||||
|
rules={[{ required: true }]}
|
||||||
|
extra="数字越小优先级越高"
|
||||||
|
>
|
||||||
|
<InputNumber min={1} max={999} />
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExpressionModal;
|
||||||
@ -44,6 +44,8 @@ import {
|
|||||||
} from './constants';
|
} from './constants';
|
||||||
import './index.less';
|
import './index.less';
|
||||||
import {NodeDefinitionResponse, NodeDesignDataResponse} from "@/pages/Workflow/NodeDesign/types";
|
import {NodeDefinitionResponse, NodeDesignDataResponse} from "@/pages/Workflow/NodeDesign/types";
|
||||||
|
import ExpressionModal from './components/ExpressionModal';
|
||||||
|
import { EdgeCondition } from './types';
|
||||||
|
|
||||||
const WorkflowDesign: React.FC = () => {
|
const WorkflowDesign: React.FC = () => {
|
||||||
const {id} = useParams<{ id: string }>();
|
const {id} = useParams<{ id: string }>();
|
||||||
@ -60,6 +62,8 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
const [isNodeDefinitionsLoaded, setIsNodeDefinitionsLoaded] = useState(false);
|
const [isNodeDefinitionsLoaded, setIsNodeDefinitionsLoaded] = useState(false);
|
||||||
const [forceUpdate, setForceUpdate] = useState(false);
|
const [forceUpdate, setForceUpdate] = useState(false);
|
||||||
const [scale, setScale] = useState(1);
|
const [scale, setScale] = useState(1);
|
||||||
|
const [expressionModalVisible, setExpressionModalVisible] = useState(false);
|
||||||
|
const [selectedEdge, setSelectedEdge] = useState<Edge | null>(null);
|
||||||
|
|
||||||
// 初始化图形
|
// 初始化图形
|
||||||
const initGraph = () => {
|
const initGraph = () => {
|
||||||
@ -531,7 +535,7 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
|
|
||||||
// 节点点击事件
|
// 节点点击事件
|
||||||
graph.on('node:click', ({node}) => {
|
graph.on('node:click', ({node}) => {
|
||||||
// <EFBFBD><EFBFBD>取当前选中的节点
|
// 取当前选中的节点
|
||||||
const selectedNode = graph.getSelectedCells()[0];
|
const selectedNode = graph.getSelectedCells()[0];
|
||||||
|
|
||||||
// 如果有其他节点被选中,恢复其样式
|
// 如果有其他节点被选中,恢复其样式
|
||||||
@ -867,6 +871,15 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
port.setAttribute('style', 'visibility: hidden');
|
port.setAttribute('style', 'visibility: hidden');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 添加边的双击事件
|
||||||
|
graph.on('edge:dblclick', ({ edge }) => {
|
||||||
|
const sourceNode = graph.getCellById(edge.getSourceCellId());
|
||||||
|
if (sourceNode.getProp('nodeType') === 'GATEWAY_NODE') {
|
||||||
|
setSelectedEdge(edge);
|
||||||
|
setExpressionModalVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理复制操作
|
// 处理复制操作
|
||||||
@ -893,7 +906,7 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
message.success('已剪切');
|
message.success('已剪切');
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理粘贴操<EFBFBD><EFBFBD><EFBFBD>
|
// 处理粘贴操作
|
||||||
const handlePaste = () => {
|
const handlePaste = () => {
|
||||||
if (!graph) return;
|
if (!graph) return;
|
||||||
if (graph.isClipboardEmpty()) {
|
if (graph.isClipboardEmpty()) {
|
||||||
@ -962,7 +975,7 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
return port?.id;
|
return port?.id;
|
||||||
};
|
};
|
||||||
|
|
||||||
// 获取源节点的输出端口(一定<EFBFBD><EFBFBD><EFBFBD>out组)
|
// 获取源节点的输出端口(一定是out组)
|
||||||
const sourcePort = getPortByGroup(sourceNode, 'out');
|
const sourcePort = getPortByGroup(sourceNode, 'out');
|
||||||
|
|
||||||
// 获取目标节点的输入端口(一定是in组)
|
// 获取目标节点的输入端口(一定是in组)
|
||||||
@ -1123,48 +1136,35 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取所有节点和边的数据
|
// 获取所有节点和边的数据
|
||||||
const nodes = graph.getNodes().map(node => {
|
const nodes = graph.getNodes().map(node => ({
|
||||||
const nodeType = node.getProp('nodeType');
|
id: node.id,
|
||||||
const graphData = node.getProp('graph') || {};
|
nodeCode: node.getProp('nodeType'),
|
||||||
const position = node.getPosition();
|
nodeType: node.getProp('nodeType'),
|
||||||
const {
|
nodeName: node.attr('label/text'),
|
||||||
uiVariables,
|
uiVariables: {
|
||||||
panelVariables,
|
...node.getProp('graph')?.uiVariables,
|
||||||
localVariables,
|
position: node.getPosition()
|
||||||
formVariablesSchema,
|
},
|
||||||
...rest
|
panelVariables: node.getProp('graph')?.panelVariables,
|
||||||
} = graphData;
|
localVariables: node.getProp('graph')?.localVariables,
|
||||||
return {
|
formVariablesSchema: node.getProp('graph')?.formVariablesSchema
|
||||||
id: node.id,
|
|
||||||
nodeCode: nodeType,
|
|
||||||
nodeType: nodeType,
|
|
||||||
nodeName: node.attr('label/text'),
|
|
||||||
uiVariables: {
|
|
||||||
...uiVariables,
|
|
||||||
position: position
|
|
||||||
},
|
|
||||||
panelVariables,
|
|
||||||
localVariables,
|
|
||||||
formVariablesSchema
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
const edges = graph.getEdges().map(edge => ({
|
|
||||||
id: edge.id,
|
|
||||||
from: edge.getSourceCellId(),
|
|
||||||
to: edge.getTargetCellId(),
|
|
||||||
name: edge.getLabels()?.[0]?.attrs?.label?.text || '',
|
|
||||||
config: {
|
|
||||||
type: 'sequence'
|
|
||||||
}
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 收集并合并所有节点的 formVariablesSchema
|
const edges = graph.getEdges().map(edge => {
|
||||||
const allFormSchemas = nodes
|
const sourceNode = graph.getCellById(edge.getSourceCellId());
|
||||||
.map(node => node.formVariablesSchema)
|
const condition = edge.getProp('condition');
|
||||||
.filter(schema => schema); // 过滤掉空值
|
|
||||||
|
return {
|
||||||
const mergedFormSchema = mergeFormVariablesSchemas(allFormSchemas);
|
id: edge.id,
|
||||||
|
from: edge.getSourceCellId(),
|
||||||
|
to: edge.getTargetCellId(),
|
||||||
|
name: edge.getLabels()?.[0]?.attrs?.label?.text || '',
|
||||||
|
config: {
|
||||||
|
type: 'sequence',
|
||||||
|
condition: condition || undefined
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
// 构建保存数据
|
// 构建保存数据
|
||||||
const saveData = {
|
const saveData = {
|
||||||
@ -1172,36 +1172,56 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
graph: {
|
graph: {
|
||||||
nodes,
|
nodes,
|
||||||
edges
|
edges
|
||||||
},
|
}
|
||||||
formVariablesSchema: mergedFormSchema
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// 调用保存接口
|
// 调用保存接口
|
||||||
await saveDefinition(saveData);
|
await saveDefinition(saveData);
|
||||||
|
|
||||||
// 使用 Modal.confirm 显示操作选择
|
message.success('保存成功');
|
||||||
Modal.confirm({
|
|
||||||
title: '保存成功',
|
|
||||||
content: '流程设计已保存成功,请选择下一步操作',
|
|
||||||
okText: '继续设计',
|
|
||||||
cancelText: '返回列表',
|
|
||||||
onOk: () => {
|
|
||||||
// 重新加载设计数据
|
|
||||||
if (id) {
|
|
||||||
loadDefinitionDetail(graph, id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onCancel: () => {
|
|
||||||
// 返回列表页
|
|
||||||
navigate('/workflow/definition');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存流程失败:', error);
|
console.error('保存流程失败:', error);
|
||||||
message.error('保存流程失败');
|
message.error('保存流程失败');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 处理条件更新
|
||||||
|
const handleConditionUpdate = (condition: EdgeCondition) => {
|
||||||
|
if (!selectedEdge) return;
|
||||||
|
|
||||||
|
// 更新边的属性
|
||||||
|
selectedEdge.setProp('condition', condition);
|
||||||
|
|
||||||
|
// 更新边的标签显示
|
||||||
|
const labelText = condition.type === 'EXPRESSION'
|
||||||
|
? condition.expression
|
||||||
|
: '默认路径';
|
||||||
|
|
||||||
|
selectedEdge.setLabels([{
|
||||||
|
attrs: {
|
||||||
|
label: {
|
||||||
|
text: labelText,
|
||||||
|
fill: '#333',
|
||||||
|
fontSize: 12
|
||||||
|
},
|
||||||
|
rect: {
|
||||||
|
fill: '#fff',
|
||||||
|
stroke: '#ccc',
|
||||||
|
rx: 3,
|
||||||
|
ry: 3,
|
||||||
|
padding: 5
|
||||||
|
}
|
||||||
|
},
|
||||||
|
position: {
|
||||||
|
distance: 0.5,
|
||||||
|
offset: 0
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
setExpressionModalVisible(false);
|
||||||
|
setSelectedEdge(null);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="workflow-design">
|
<div className="workflow-design">
|
||||||
<div className="header">
|
<div className="header">
|
||||||
@ -1375,6 +1395,17 @@ const WorkflowDesign: React.FC = () => {
|
|||||||
onOk={handleNodeConfigUpdate}
|
onOk={handleNodeConfigUpdate}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{selectedEdge && (
|
||||||
|
<ExpressionModal
|
||||||
|
visible={expressionModalVisible}
|
||||||
|
edge={selectedEdge}
|
||||||
|
onOk={handleConditionUpdate}
|
||||||
|
onCancel={() => {
|
||||||
|
setExpressionModalVisible(false);
|
||||||
|
setSelectedEdge(null);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user