Commit 596531c6 authored by 邓超's avatar 邓超

fix: 工作流编辑器支持拖拽

parent db87f192
Pipeline #63462 waiting for manual action with stages
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
This diff was suppressed by a .gitattributes entry.
......@@ -2,7 +2,7 @@
* @Author: dengchao 754083046@qq.com
* @Date: 2022-11-02 14:37:53
* @LastEditors: dengchao 754083046@qq.com
* @LastEditTime: 2022-11-07 17:32:35
* @LastEditTime: 2022-11-09 15:36:19
* @FilePath: \maintenance\src\components\RuleConfig\index.jsx
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
......@@ -60,10 +60,10 @@ const RuleConfig = props => {
return (
<div>
<Modal
title="节点流转规则配置"
title="规则配置"
visible={visible}
onOk={onSave}
width="860px"
width="740px"
onCancel={handleCancel}
maskClosable={false}
destroyOnClose
......@@ -95,7 +95,7 @@ const RuleConfig = props => {
<li>
<p>请从左侧面板选择字段或选项</p>
<p>
{'支持'} <i>英文</i> {'模式下运算符(+、-、*、/、>、<、==、!=、<=、>=)及函数'}
{'支持'} <i>英文</i> {'模式下运算符(+、-、*、/、>、<、==、!=、<=、>=)'}
</p>
</li>
......@@ -103,8 +103,10 @@ const RuleConfig = props => {
<>
<li>
<p>参考场景:</p>
<p>当报销金额大于10000时,才能进入所选节点,则可将流转条件设置为:</p>
<p>{'报销金额>10000'}</p>
<p>当报销金额大于1000时,才能进入所选节点,则可将流转条件设置为:</p>
<p>
<em>{'报销金额 > 1000'}</em>
</p>
</li>
</>
) : (
......
.configContent {
height: 600px;
height: 500px;
display: flex;
.leftTree {
width: 250px;
width: 180px;
height: 100%;
margin-right: 10px;
overflow-y: scroll;
......@@ -12,28 +12,40 @@
.rightContent {
.title {
width: 100%;
background-color: #80C3FF;
font-size: 18px;
font-weight: 700;
background-color: #E6F3FF;
color: #1890FF;
font-size: 14px;
padding: 5px 10px;
border: 1px solid #EEF6FE;
}
.textAreaBox {
// height: 400px;
// overflow-y: scroll;
.textArea {
min-height: 400px;
border: 1px solid #ccc;
textArea {
// min-height: 200px;
max-height: 360px !important;
min-height: 360px !important;
border: 1px solid transparent;
background-color: #F0F2F6;
}
}
.tipBox {
padding-top: 12px;
padding-left: 25px;
li {
list-style: disc;
color: #AEAEAE;
}
em {
font-style: normal;
color: #1890FF;
}
i {
font-style: normal;
color: red;
......
/* eslint-disable global-require */
import React, { useState, useEffect } from 'react';
import { Button, Modal, notification, Spin, Tooltip } from 'antd';
import lodash from 'lodash';
import { SaveNodeChange, GetFlowNode } from '@/services/workflow/workflow';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { ExclamationCircleOutlined, TrophyOutlined } from '@ant-design/icons';
import { Prompt } from 'react-router-dom';
import * as go from 'gojs';
import styles from '../workflow.less';
......@@ -19,16 +20,9 @@ import gatewayParallel from '@/assets/images/workFlow/gatewayParallel.png';
import gatewayJoin from '@/assets/images/workFlow/gatewayJoin.png';
const { confirm } = Modal;
let diagram = null;
const nodeTypeList = [
{ nodeTypeName: '开始节点', NodeType: '1', src: nodeStart },
{ nodeTypeName: '普通节点', NodeType: '0', src: nodeGeneral },
{ nodeTypeName: '结束节点', NodeType: '2', src: nodeEnd },
];
const gatewayList = [
{ nodeTypeName: '条件网关', NodeType: '20', src: gatewayCondition },
{ nodeTypeName: '并行网关', NodeType: '22', src: gatewayParallel },
{ nodeTypeName: '汇合网关', NodeType: '21', src: gatewayJoin },
];
let myPaletteNode = null;
let myPaletteGateway = null;
let myOverview = null;
const FlowChart = props => {
const { flowData, flowID, chartLoading, leaveCallBack } = props;
const [visible, setVisible] = useState(false);
......@@ -50,7 +44,6 @@ const FlowChart = props => {
}); // 组件内得流程图数据
const [showLeaveTip, setShowLeaveTip] = useState(false); // 离开路由是否又提醒
const [newSerialNo, setNewSerialNo] = useState(0);
const [nodeLength, setNodeLength] = useState();
const [buttonLoading, setButtonLoading] = useState(); // 发布按钮保存loading
const objGo = go.GraphObject.make;
// 监听删除,给删除数组里添加删除id
......@@ -68,6 +61,8 @@ const FlowChart = props => {
useEffect(() => {
// 初始化流程图
init();
initPalette();
myOverview = objGo(go.Overview, 'myOverviewDiv', { observed: diagram });
// 监听节点或线的删除事件
diagram.addDiagramListener('SelectionDeleted', e => {
e.subject.each(n => {
......@@ -99,7 +94,37 @@ const FlowChart = props => {
console.log(e, e.subject.data, 'fasdfasdgds');
diagram.model.updateTargetBindings(e.subject.data);
});
diagram.addDiagramListener('externalobjectsdropped', e => {
const list = JSON.parse(diagram.model.toJson()).nodeDataArray;
console.log(list, 'list');
let newNum;
let newKey;
if (list.length > 0) {
// eslint-disable-next-line prefer-spread
newNum = Math.max.apply(Math, list.map(item => item.SerialNo)) + 1;
// eslint-disable-next-line prefer-spread
newKey = Math.max.apply(Math, list.map(item => item.key)) + 1;
} else {
newKey = 1;
newNum = 1;
}
console.log(e);
e.subject.each(n => {
// 得到从Palette拖过来的节点
console.log(n.data.key);
let nodeData = diagram.model.findNodeDataForKey(n.data.key);
nodeData.NodeName = `${n.data.NodeName}${newKey}`;
nodeData.SerialNo = newNum;
// nodeData.key = newKey;
nodeData.NodeId = newKey;
nodeData.nodeDetail = JSON.stringify(nodeData);
diagram.model.updateTargetBindings(nodeData);
diagram.model.setDataProperty(nodeData, 'key', newKey);
});
setAddNodes([...AddNodes, newKey]);
leaveTip();
});
// diagram.addDiagramListener('SelectionDeleted', e => {
// });
......@@ -207,13 +232,181 @@ const FlowChart = props => {
leaveCallBack(true);
diagram.commandHandler.deleteSelection();
};
// 创建面板
const makePalette = () => {};
const animateFadeDown = e => {
let diagrams = e.diagram;
let animation = new go.Animation();
animation.isViewportUnconstrained = true; // 所以图表定位规则让动画在屏幕外开始
animation.easing = go.Animation.EaseOutExpo;
animation.duration = 900;
// 淡入“向下”,换句话说,从上方淡入
animation.add(diagrams, 'position', diagrams.position.copy().offset(0, 200), diagrams.position);
animation.add(diagrams, 'opacity', 0, 1);
animation.start();
};
// 初始化拖拽面板
const initPalette = () => {
myPaletteNode = objGo(go.Palette, 'myPaletteNode', {
// 代替默认动画,使用自定义淡入淡出
'animationManager.initialAnimationStyle': go.AnimationManager.None,
InitialAnimationStarting: animateFadeDown, // 相反,使用此功能制作动画
// nodeTemplateMap: diagram.nodeTemplateMap, // 分享 myDiagram 使用的模板
scale: '1',
nodeSelectionAdornmentTemplate: objGo(
go.Adornment,
'Auto',
objGo(go.Shape, 'Rectangle', { fill: 'white', stroke: null }),
), // 去掉节点点击时的边框颜色
model: new go.GraphLinksModel([
// 指定调色板的内容
{
category: 'nodeStart',
NodeName: '开始节点',
NodeType: '1',
SerialNo: 0,
aheadHandle: 1,
NodeHandling: 1,
RuleList: [],
roleList: [],
},
{
category: 'nodeGeneral',
NodeName: '普通节点',
NodeType: '0',
SerialNo: 0,
aheadHandle: 1,
NodeHandling: 1,
RuleList: [],
roleList: [],
},
{
category: 'nodeEnd',
NodeName: '结束节点',
NodeType: '2',
SerialNo: 0,
aheadHandle: 1,
NodeHandling: 1,
RuleList: [],
roleList: [],
},
]),
});
myPaletteNode.nodeTemplate = objGo(
go.Node,
'Auto',
new go.Binding('location', 'points', go.Point.parse).makeTwoWay(go.Point.stringify),
// 节点样式配置
objGo(
go.Panel,
{ width: 116, height: 50 },
objGo(
go.Picture,
{ width: 116, height: 50 },
new go.Binding('source', 'NodeType', v => {
switch (v) {
case '1':
return require('../../../../../assets/images/workFlow/icon1.png');
case '2':
return require('../../../../../assets/images/workFlow/icon3.png');
case '0':
return require('../../../../../assets/images/workFlow/icon2.png');
// case '4':
// return cc;
case '20':
return require('../../../../../assets/images/workFlow/gateWayicon1.png');
case '21':
return require('../../../../../assets/images/workFlow/gateWayicon3.png');
case '22':
return require('../../../../../assets/images/workFlow/gateWayicon2.png');
default:
return null;
}
}),
),
),
);
myPaletteGateway = objGo(go.Palette, 'myPaletteGateway', {
// 代替默认动画,使用自定义淡入淡出
'animationManager.initialAnimationStyle': go.AnimationManager.None,
InitialAnimationStarting: animateFadeDown, // 相反,使用此功能制作动画
// nodeTemplateMap: diagram.nodeTemplateMap, // 分享 myDiagram 使用的模板
scale: '1',
nodeSelectionAdornmentTemplate: objGo(
go.Adornment,
'Auto',
objGo(go.Shape, 'Rectangle', { fill: 'white', stroke: null }),
), // 去掉节点点击时的边框颜色
model: new go.GraphLinksModel([
// 指定调色板的内容
{
category: 'gatewayCondition',
NodeName: '条件网关',
NodeType: '20',
SerialNo: 0,
aheadHandle: 1,
NodeHandling: 1,
RuleList: [],
roleList: [],
},
{
category: 'gatewayParallel',
NodeName: '并行网关',
NodeType: '22',
SerialNo: 0,
aheadHandle: 1,
NodeHandling: 1,
RuleList: [],
roleList: [],
},
{
category: 'gatewayJoin',
NodeName: '汇合网关',
NodeType: '21',
SerialNo: 0,
aheadHandle: 1,
NodeHandling: 1,
RuleList: [],
roleList: [],
},
]),
});
myPaletteGateway.nodeTemplate = objGo(
go.Node,
'Auto',
new go.Binding('location', 'points', go.Point.parse).makeTwoWay(go.Point.stringify),
// 节点样式配置
objGo(
go.Panel,
{ width: 116, height: 50 },
objGo(
go.Picture,
{ width: 116, height: 50 },
new go.Binding('source', 'NodeType', v => {
switch (v) {
case '20':
return require('../../../../../assets/images/workFlow/gateWayicon1.png');
case '21':
return require('../../../../../assets/images/workFlow/gateWayicon3.png');
case '22':
return require('../../../../../assets/images/workFlow/gateWayicon2.png');
default:
return null;
}
}),
),
),
);
};
// 流程图初始化
const init = () => {
diagram = objGo(go.Diagram, 'myDiagramDiv', {
'undoManager.isEnabled': true,
allowDragOut: false,
'dragSelectingTool.isEnabled': false, // 禁止多选
scrollMode: go.Diagram.InfiniteScroll, // 无限滚动
allowCopy: true, // 禁止复制
allowDrop: true,
// nodeSelectionAdornmentTemplate: objGo(
// go.Adornment,
// 'Auto',
......@@ -221,6 +414,7 @@ const FlowChart = props => {
// ), // 去掉节点点击时的边框颜色
scale: '0.8',
});
// 节点配置
diagram.nodeTemplate = objGo(
go.Node,
......@@ -276,13 +470,13 @@ const FlowChart = props => {
overflow: go.TextBlock.OverflowEllipsis,
font: 'normal 12pt serif',
},
new go.Binding('text', 'nodeDetail', v => {
const obj = JSON.parse(v);
if (obj.NodeType === '20' || obj.NodeType === '21' || obj.NodeType === '22') {
return '';
new go.Binding('visible', 'NodeType', v => {
if (v.NodeType === '20' || v.NodeType === '21' || v.NodeType === '22') {
return false;
}
return obj.NodeName;
return true;
}),
new go.Binding('text', 'NodeName'),
nodeBoxStyle('stroke', 'nodeStyle'),
),
objGo(
......@@ -405,16 +599,6 @@ const FlowChart = props => {
new go.Binding('fill', 'lineDetail', v => lineTextStyle(v)),
new go.Binding('stroke', 'lineDetail', v => lineTextStyle(v)),
),
// objGo(
// go.TextBlock, // the label text
// {
// textAlign: 'center',
// font: '10pt helvetica, arial, sans-serif',
// stroke: '#555555',
// margin: 4,
// },
// new go.Binding('text', 'text'),
// ),
objGo(
go.TextBlock,
{
......@@ -630,7 +814,6 @@ const FlowChart = props => {
default:
break;
}
setNodeLength(list.length);
console.log(newNum, newKey);
setNewSerialNo(newNum);
// 新增节点
......@@ -675,23 +858,6 @@ const FlowChart = props => {
});
return;
}
// if (modalType === 'add') {
// // 新增节点
// // 新增得key比最大得key值+1
// let newKey;
// if (keyArr.length === 0) {
// newKey = 1;
// } else {
// newKey = keyArr.reduce((num1, num2) => (num1 > num2 ? num1 : num2)) + 1;
// }
// diagram.model.addNodeData({
// key: newKey,
// NodeId: newKey,
// ...obj,
// });
// setAddNodes([...AddNodes, newKey]);
// }
// if (modalType === 'edit') {
// 编辑节点
let nodeData = diagram.model.findNodeDataForKey(nodeKey);
const {
......@@ -722,23 +888,13 @@ const FlowChart = props => {
let node = diagram.model.findLinkDataForKey(item.LineKey);
node.text = item.RuleName;
diagram.model.updateTargetBindings(node);
// if (
// item.from === nodeData.NodeId &&
// nodeData.RuleList.some(ele => ele.NextNodeId === item.to)
// ) {
// let node = diagram.model.findLinkDataForKey(item.LineKey);
// node.text = item.RuleName;
// diagram.model.updateTargetBindings(node);
// } else {
// }
});
// }
// 关闭时进行数据比对看数据是否改变
leaveTip();
setVisible(false);
};
// 关闭时进行数据比对看数据是否改变
const leaveTip = () => {
// 关闭时进行数据比对看数据是否改变
let diagramObj = JSON.parse(diagram.model.toJson());
let stageJson = {
Nodes: diagramObj.nodeDataArray,
......@@ -754,14 +910,10 @@ const FlowChart = props => {
};
// 线配置回调函数
const lineCallBack = obj => {
console.log(obj, 'obj');
console.log(LineKey, 'LineKey');
console.log(diagram, 'diagram');
let node = diagram.model.findLinkDataForKey(LineKey);
console.log(node, 'nodeData');
node.text = obj.text;
diagram.model.updateTargetBindings(node);
console.log(node, 'linkemsg');
// 关闭时进行数据比对看数据是否改变
leaveTip();
setLineVisible(false);
......@@ -852,27 +1004,9 @@ const FlowChart = props => {
<Prompt message="编辑的内容还未保存,确定要离开该页面吗?" when={showLeaveTip} />
<div className={styles.control}>
<div className={styles.nodeList}>
{nodeTypeList.map(item => (
<Tooltip placement="topLeft" title="点击插入节点" key={item.NodeType}>
<div className={styles.nodeBox} onClick={() => addNode(item.NodeType)}>
<div className={styles.nodeImg}>
<img src={item.src} alt="" />
</div>
<div className={styles.nodeTypeName}>{item.nodeTypeName}</div>
</div>
</Tooltip>
))}
<div className={styles.lineBox} />
{gatewayList.map(item => (
<Tooltip placement="topLeft" title="点击插入网关" key={item.NodeType}>
<div className={styles.nodeBox} onClick={() => addNode(item.NodeType)}>
<div className={styles.nodeImg}>
<img src={item.src} alt="" />
</div>
<div className={styles.nodeTypeName}>{item.nodeTypeName}</div>
</div>
</Tooltip>
))}
<div id="myPaletteNode" className={styles.myPaletteDiv} />
{/* <div className={styles.lineBox} /> */}
<div id="myPaletteGateway" className={styles.myPaletteDiv} />
</div>
<div className={styles.buttonList}>
{/* <Button
......@@ -892,6 +1026,7 @@ const FlowChart = props => {
</div>
</div>
<div className={styles.chartBox}>
<div id="myOverviewDiv" className={styles.myOverviewDiv} />
<Spin spinning={chartLoading}>
<div
id="myDiagramDiv"
......@@ -905,7 +1040,6 @@ const FlowChart = props => {
visible={visible}
editMsg={editMsg}
newSerialNo={newSerialNo}
nodeNum={nodeLength}
modalType={modalType}
handleCancel={() => setVisible(false)}
onSubumit={(obj, nextlinkNodes) => nodeCallBack(obj, nextlinkNodes)}
......
......@@ -41,7 +41,6 @@ const NodeModal = props => {
modalType,
editMsg,
newSerialNo,
nodeNum,
flowID,
} = props;
const [form] = Form.useForm();
......@@ -434,7 +433,11 @@ const NodeModal = props => {
/>
</Tooltip>
<Tooltip title="删除角色或机构">
<Popconfirm
<DeleteOutlined
onClick={() => delUser(record, index)}
style={{ fontSize: '16px', color: '#e86060' }}
/>
{/* <Popconfirm
title="是否删除该角色或机构?"
onConfirm={() => delUser(record, index)}
onCancel={() => message.error('取消删除')}
......@@ -442,7 +445,7 @@ const NodeModal = props => {
cancelText="否"
>
<DeleteOutlined style={{ fontSize: '16px', color: '#e86060' }} />
</Popconfirm>
</Popconfirm> */}
</Tooltip>
</Space>
</>
......@@ -676,7 +679,7 @@ const NodeModal = props => {
</div>
</div>
<div className={styles.formBox}>
<div className={styles.label}>设置规则条件,需要流转到节点:</div>
<div className={styles.label}>设置规则条件,可以流转到节点:</div>
<div className={styles.item}>
<Select
style={{ width: '100%' }}
......
......@@ -103,30 +103,55 @@
background-color: #fff;
.chartBox {
position: relative;
height: calc(100% - 62px);
.myOverviewDiv {
position: absolute;
height: 150px;
width: 300px;
right: 0;
bottom: 0;
background-color: #ccc;
z-index: 9;
}
}
.control {
display: flex;
justify-content: space-between;
height: 60px;
align-items: center;
padding: 0 10px;
.nodeList {
display: flex;
align-items: center;
.myPaletteDiv {
margin-right: 20px;
height: 60px;
width: 380px;
canvas {
height: 100%;
width: 1000px;
}
}
.lineBox {
height: 40px;
height: 50px;
width: 1px;
background-color: #ccc;
margin-right: 10px;
// margin-right: 10px;
}
.nodeBox {
display: flex;
align-items: center;
justify-content: center;
height: 40px;
height: 60px;
padding: 0 10px;
border-radius: 10px;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment