This commit is contained in:
dengqichen 2024-12-24 16:53:24 +08:00
parent 3543906817
commit d7c6af621b
2 changed files with 186 additions and 62 deletions

View File

@ -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;

View File

@ -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>
); );
}; };