/* eslint-disable global-require */ import React, { useState, useEffect, useRef } from 'react'; import { useHistory, Prompt } from 'react-router-dom'; import { Button, Modal, notification, Spin, Empty, Tooltip, message, TreeSelect } from 'antd'; import lodash from 'lodash'; import { SaveNodeChange, GetFlowNode, FlowNodeSave, DeleteFlowNode, DeleteFlowNodes, SaveWorkFlowImage, } from '@/services/workflow/workflow'; import { ExclamationCircleOutlined, TrophyOutlined } from '@ant-design/icons'; import * as go from 'gojs'; import styles from './FlowBoard.less'; // import styles from '../workflow.less'; import NodeModal from './flowChartComponents/NodeModal'; import LineModal from './flowChartComponents/LineModal'; // import imgUrl from '@/assets/images/icons/closeBlue.png'; import nodeEnd from '@/assets/images/workFlow/nodeEnd.svg'; import nodeGeneral from '@/assets/images/workFlow/nodeGeneral.svg'; import nodeStart from '@/assets/images/workFlow/nodeStart.svg'; // import cc from '@/assets/images/workFlow/cc.png'; import gatewayCondition from '@/assets/images/workFlow/gatewayCondition.svg'; import gatewayParallel from '@/assets/images/workFlow/gatewayParallel.svg'; import gatewayJoin from '@/assets/images/workFlow/gatewayJoin.svg'; const { confirm } = Modal; const { TreeNode } = TreeSelect; let diagram = null; let myPaletteNode = null; let myPaletteGateway = null; let myPaletteSubprocess = null; let myOverview = null; const FlowChart = props => { const history = useHistory(); const { flowData, flowID, chartLoading, msg, treeVisible, activeKey, flowTree, scrollTop, } = props; const [visible, setVisible] = useState(false); const [lineVisible, setLineVisible] = useState(false); const [editMsg, setEditMsg] = useState({}); // 编辑节点的信息 const [lineMsg, setLineMsg] = useState({}); const [modalType, setModalType] = useState(''); // 存入弹窗是编辑还是新增 const [LineKey, setLineKey] = useState(''); // 存入编辑线id const [nodeKey, setNodeKey] = useState(''); // 存入编辑节点的key const [DeleteNodes, setDeleteNodes] = useState([]); // 删除节点数组 const [DeleteLines, setDeleteLines] = useState([]); // 删除线数组 const [deleteLine, setDeleteLine] = useState(); // 删除的线id const [deleteNode, setDeleteNode] = useState(); // 删除的节点id const [AddNodes, setAddNodes] = useState([]); // 新增数组 const [initFlowData, setInitFlowData] = useState({}); // 初始数据,用来比对是否有修改流程图 const [currentFlowData, setCurrentFlowData] = useState({ Nodes: [], Lines: [], }); // 组件内得流程图数据 const [showLeaveTip, setShowLeaveTip] = useState(false); // 离开路由是否又提醒 const [buttonLoading, setButtonLoading] = useState(); // 发布按钮保存loading const [selectValue, setSelectValue] = useState(); const [flag, setFlag] = useState(0); const currentNode = useRef(); const afterNodes = useRef(new Map([])); // 当前节点后所有节点 const limitFinshNodes = useRef([new Set([])]); const objGo = go.GraphObject.make; useEffect(() => { if (treeVisible) { setVisible(false); } }, [treeVisible]); // 监听删除,给删除数组里添加删除id useEffect(() => { if (deleteLine) { setDeleteLines([...DeleteLines, deleteLine]); } }, [deleteLine]); useEffect(() => { if (deleteNode) { setDeleteNodes([...DeleteNodes, deleteNode]); } }, [deleteNode]); // 初始化 useEffect(() => { // 初始化流程图 init(); initPalette(); myOverview = objGo(go.Overview, 'myOverviewDiv', { observed: diagram }); // 监听节点或线的删除事件 diagram.addDiagramListener('SelectionDeleted', e => { let delNodes = []; let delLinks = []; e.subject.each(n => { if (n.data.LineId) { delLinks.push(n.data.LineId); } if (n.data.ActivityId) { delNodes.push(n.data.ActivityId); } // 如果删除得节点不是新增得就给id放入到删除节点数组中 if (n.data.NodeId && !AddNodes.some(item => item === n.data.NodeId)) { setTimeout(() => { setDeleteNode(n.data.NodeId); }, 0); } if (n.data.LineKey) { setTimeout(() => { setDeleteLine(n.data.LineId); }, 0); } }); if (delNodes.length === 0) { return; } DeleteFlowNodes({ ActivityIds: delNodes, LineIds: delLinks }).then(res => { if (res.code === 0) { message.success('删除成功'); } else { message.error(res.msg); } }); console.log(delNodes, delLinks, 'fffff'); }); // 监听节点或线的删除前事件 diagram.commandHandler.canDeleteSelection = () => // 用例获取选中的节点或线 diagram.selection.all(e => { // 判断是否存在不允许删除的节点或线 showDeleteConfirm(e.data); return false; }); // 监听线,连接线的时候加上text属性 diagram.addDiagramListener('LinkDrawn', e => { // e.subject.data.text = ''; e.subject.data.lineDetail = JSON.stringify(e.subject.data); console.log(e, e.subject.data, 'fasdfasdgds'); diagram.model.updateTargetBindings(e.subject.data); // leaveCallBack(true); }); // 监听节点拖拽到画布事件 diagram.addDiagramListener('externalobjectsdropped', e => { afterNodes.current = new Map([]); 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.NodeAliasName = nodeData.NodeName; nodeData.SerialNo = newNum; // nodeData.key = newKey; nodeData.NodeId = newKey; nodeData.nodeDetail = JSON.stringify(nodeData); console.log(nodeData); diagram.model.updateTargetBindings(nodeData); diagram.model.setDataProperty(nodeData, 'key', newKey); currentNode.current = nodeData; setNodeKey(nodeData.key); setEditMsg(nodeData); setModalType('edit'); setVisible(true); }); setAddNodes([...AddNodes, newKey]); leaveTip(); }); // diagram.addDiagramListener('SelectionDeleted', e => { // }); }, []); useEffect(() => { if (flowData) { console.log(flowData, 'msgmsgmsg'); // 每次切换时清空删除得id数组跟新增得id数组 setDeleteNodes([]); setDeleteLines([]); setAddNodes([]); setDeleteNode(''); setDeleteLine(''); setEditMsg({}); let dataList = lodash.cloneDeep(flowData); console.log(dataList, 'dataList'); setCurrentFlowData(dataList); setShowLeaveTip(false); setVisible(false); } }, [flowData]); // 存入在树形流程中选择得流程数据 useEffect(() => { let nodeDataArray = []; let linkDataArray = []; // 处理老数据,让老数据可以正常展示 limitFinshNodes.current = new Set([]); nodeDataArray = currentFlowData.Nodes.map((item, index) => { if (item.FlowTimerList.length > 0) { item.FlowTimerList.forEach(ele => { limitFinshNodes.current.add(ele.EndNode); }); } let obj; obj = item; obj.key = item.NodeId; if (!obj.NodeAliasName) { obj.NodeAliasName = obj.NodeName; } obj.nodeDetail = JSON.stringify(obj); obj.CarbonCopyPeopleList = obj.CarbonCopyPeopleList.map(ele => ({ label: ele.userName, value: ele.userID, })); if (obj.points === '') { if (obj.NodeType === '1') { obj.points = `${(index * 200).toString()}" 100"`; } else { obj.points = `${(index * 200).toString()}" -22"`; } } return obj; }); linkDataArray = currentFlowData.Lines.map(item => { let obj; obj = item; obj.LineKey = item.LineId; obj.lineDetail = JSON.stringify(obj); return obj; }); // 保存初始数据 setInitFlowData( JSON.parse( JSON.stringify({ Nodes: nodeDataArray, Lines: linkDataArray, }), ), ); diagram.model = go.Model.fromJson({ linkFromPortIdProperty: 'fromPort', // 所需信息: linkToPortIdProperty: 'toPort', // 标识数据属性名称 nodeDataArray, linkDataArray, }); // 初次选中 if (nodeDataArray?.length > 0) { currentNode.current = diagram.model.findNodeDataForKey( nodeDataArray[nodeDataArray.length - 1].NodeId, ); setNodeKey(currentNode.current.key); setEditMsg(currentNode.current); setModalType('edit'); setVisible(true); } // 修改复制后节点内容 diagram.model.copyNodeDataFunction = (obj, model) => { let copyObj = lodash.cloneDeep(obj); console.log(copyObj, 'copyObj'); copyObj.FlowTimerList.forEach(item => { item.key = item.ID; delete item.ID; }); delete copyObj.ActivityId; delete copyObj.FlowNodeExtendId; return copyObj; }; // 修改复制后线内容 diagram.model.copyLinkDataFunction = (obj, model) => { let copyObj = lodash.cloneDeep(obj); delete copyObj.LineId; return copyObj; }; diagram.model.linkKeyProperty = 'LineKey'; diagram.model.makeUniqueLinkKeyFunction = (model, data) => { let i = model.linkDataArray.length * 2 + 2; while (model.findLinkDataForKey(i) !== null) i += 2; return i; }; }, [currentFlowData]); // 删除提醒 const showDeleteConfirm = val => { confirm({ title: '确定要删除所选中的节点吗?', icon: <ExclamationCircleOutlined />, content: '', okText: '是', okType: 'danger', cancelText: '否', onOk() { delNode(val); }, onCancel() {}, }); }; // 删除节点 const delNode = val => { setShowLeaveTip(true); // leaveCallBack(true); diagram.commandHandler.deleteSelection(); // if (val.LineId) { // diagram.commandHandler.deleteSelection(); // return; // } // DeleteFlowNode({ activityId: val.ActivityId }).then(res => { // if (res.code === 0) { // message.success('删除成功'); // diagram.commandHandler.deleteSelection(); // } else { // message.error(res.msg); // } // }); }; 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 = () => { const defaultField = { aheadHandle: 1, NodeHandling: 1, RuleList: [], roleList: [], CarbonCopyPeopleList: [], ExtendPageList: [], FlowTimerList: [], TurnOnCc: 0, NodeAliasName: '', TableName: '', Fields: '', WebPage: '', FeedbackName: '', Transferable: 0, EventsInformation: 0, IsSendMessage: 1, IsSave: 0, AutoClose: '否', HalfwayClose: 0, RollbackNode: '(上一节点)', Rollbackable: false, }; 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, Handover: '移交选择人', ...defaultField, }, { category: 'nodeGeneral', NodeName: '普通节点', NodeType: '0', SerialNo: 0, Handover: '移交选择人', ...defaultField, }, { category: 'nodeEnd', NodeName: '结束节点', NodeType: '2', SerialNo: 0, Handover: '自处理', ...defaultField, }, ]), }); myPaletteNode.nodeTemplate = objGo( go.Node, 'Auto', new go.Binding('location', 'points', go.Point.parse).makeTwoWay(go.Point.stringify), // 节点样式配置 objGo( go.Panel, { width: 108, height: 42 }, objGo( go.Picture, { width: 108, height: 42 }, new go.Binding('source', 'NodeType', v => { switch (v) { case '1': return require('../../../../../assets/images/workFlow/icon1.svg'); case '2': return require('../../../../../assets/images/workFlow/icon3.svg'); case '0': return require('../../../../../assets/images/workFlow/icon2.svg'); 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, ...defaultField, }, { category: 'gatewayParallel', NodeName: '并行网关', NodeType: '22', SerialNo: 0, ...defaultField, }, { category: 'gatewayJoin', NodeName: '汇合网关', NodeType: '21', SerialNo: 0, ...defaultField, }, ]), }); myPaletteGateway.nodeTemplate = objGo( go.Node, 'Auto', new go.Binding('location', 'points', go.Point.parse).makeTwoWay(go.Point.stringify), // 节点样式配置 objGo( go.Panel, { width: 108, height: 42 }, objGo( go.Picture, { width: 108, height: 42 }, new go.Binding('source', 'NodeType', v => { switch (v) { case '20': return require('../../../../../assets/images/workFlow/gateWayicon1.svg'); case '21': return require('../../../../../assets/images/workFlow/gateWayicon3.svg'); case '22': return require('../../../../../assets/images/workFlow/gateWayicon2.svg'); default: return null; } }), ), ), ); myPaletteSubprocess = objGo(go.Palette, 'myPaletteSubprocess', { // 代替默认动画,使用自定义淡入淡出 '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: '30', SerialNo: 0, ...defaultField, }, ]), }); myPaletteSubprocess.nodeTemplate = objGo( go.Node, 'Auto', new go.Binding('location', 'points', go.Point.parse).makeTwoWay(go.Point.stringify), // 节点样式配置 objGo( go.Panel, { width: 108, height: 42 }, objGo( go.Picture, { width: 108, height: 42 }, new go.Binding('source', 'NodeType', v => { switch (v) { case '30': return require('../../../../../assets/images/workFlow/subprocessicon.svg'); default: return null; } }), ), ), ); }; // 流程图初始化 const init = () => { diagram = objGo(go.Diagram, 'myDiagramDiv', { 'undoManager.isEnabled': true, allowDragOut: false, 'dragSelectingTool.isEnabled': false, // 禁止多选 // 'grid.visible': true, scrollMode: go.Diagram.InfiniteScroll, // 无限滚动 allowCopy: true, // 禁止复制 allowDrop: true, // nodeSelectionAdornmentTemplate: objGo( // go.Adornment, // 'Auto', // objGo(go.Shape, 'Rectangle', { fill: 'white', stroke: null }), // ), // 去掉节点点击时的边框颜色 scale: '0.8', }); diagram.grid.gridCellSize = new go.Size(10, 10); diagram.toolManager.draggingTool.isGridSnapEnabled = true; // 节点配置 diagram.nodeTemplate = objGo( go.Node, 'Auto', new go.Binding('location', 'points', go.Point.parse).makeTwoWay(go.Point.stringify), // 节点样式配置 objGo( go.Panel, nodeBoxStyle('width'), nodeBoxStyle('height'), objGo( go.Picture, new go.Binding('source', 'NodeType', v => { switch (v) { case '1': return nodeStart; case '2': return nodeEnd; case '0': return nodeGeneral; // case '4': // return cc; case '20': return gatewayCondition; case '21': return gatewayJoin; case '22': return gatewayParallel; case '30': return require('../../../../../assets/images/workFlow/nodesubprocess.svg'); default: return null; } }), nodeBoxStyle('width'), nodeBoxStyle('height'), ), objGo( go.Panel, 'Horizontal', nodeBoxStyle('height'), { alignment: go.Spot.Center }, objGo( go.Panel, 'Vertical', // 节点文案 nodeBoxStyle('width'), objGo( go.TextBlock, { maxSize: new go.Size(120, NaN), maxLines: 1, alignment: go.Spot.Center, margin: new go.Margin(0, 15, 0, 15), overflow: go.TextBlock.OverflowEllipsis, font: 'normal 12pt Microsoft YaHei', }, new go.Binding('visible', 'NodeType', v => { if (v.NodeType === '20' || v.NodeType === '21' || v.NodeType === '22') { return false; } return true; }), new go.Binding('text', 'NodeAliasName'), nodeBoxStyle('stroke', 'nodeStyle'), ), objGo( go.TextBlock, { alignment: go.Spot.Center, maxLines: 2, overflow: go.TextBlock.OverflowEllipsis, font: 'normal 12pt Microsoft YaHei', }, new go.Binding('spacingAbove', 'roleList', v => (v?.length > 0 ? 5 : 0)), new go.Binding('height', 'roleList', v => (v?.length > 0 ? 30 : 0)), new go.Binding('margin', 'roleList', v => v?.length > 0 ? new go.Margin(10, 10, 0, 10) : 0, ), new go.Binding('text', 'nodeDetail', v => { const obj = JSON.parse(v); if (obj.NodeType === '20' || obj.NodeType === '21' || obj.NodeType === '22') { return ''; } if (obj.roleList?.length === 0) { return ''; } return obj.roleList.map(item => item.roleName).join(','); }), nodeBoxStyle('stroke', 'roleStyle'), ), ), ), ), // 我们的小命名端口,每侧一个: makePort('T', go.Spot.Top), makePort('L', go.Spot.Left), makePort('R', go.Spot.Right), makePort('B', go.Spot.Bottom), { // 节点之间线得连接 linkValidation(fromnode, fromport, tonode, toport, thisLink) { // 并行网关不让连接汇合网关 if (fromnode.data.NodeType === '22' && tonode.data.NodeType === '21') { return false; } // 条件网关不让连接条件网关 if (fromnode.data.NodeType === '20' && tonode.data.NodeType === '20') { return false; } // 汇合网关不让连条件网关 if (fromnode.data.NodeType === '21' && tonode.data.NodeType === '20') { return false; } return true; }, // 处理鼠标进入/离开事件以显示/隐藏端口 mouseEnter(e, node) { showSmallPorts(node, true); }, mouseLeave(e, node) { showSmallPorts(node, false); }, click(e, node) { handlerDC(e, node); }, // 处理双击 doubleClick(e, node) { // 双击事件 // handlerDC(e, node); // 双击执行的方法 }, selectionChanged: node => { // console.log(node.data, 'nodenodenode'); }, toolTip: objGo( 'ToolTip', objGo( go.TextBlock, { margin: 4 }, new go.Binding('text', 'nodeDetail', v => { const obj = JSON.parse(v); return `节点名称:${obj.NodeName}\n${ obj.roleList.length > 0 ? '承办:' : '' }${obj.roleList.map(item => item.roleName).join(',')}`; }), ), ), }, ); // 链接设置 diagram.linkTemplate = objGo( go.Link, { routing: go.Link.Orthogonal, curve: go.Link.JumpOver, corner: 5, toShortLength: 4, selectionAdornmentTemplate: objGo( go.Adornment, objGo(go.Shape, { isPanelMain: true, stroke: '#faad14', strokeWidth: 2 }), // 修改线颜色和大小 objGo(go.Shape, { toArrow: 'Standard', fill: '#faad14', stroke: '#faad14' }), // 修改线箭头的颜色和大小 ), }, new go.Binding('points').makeTwoWay(), objGo( go.Shape, // 链接路径形状 { isPanelMain: true, strokeWidth: 2, }, new go.Binding('stroke', 'from', v => lineStyle(v, 'stroke')), new go.Binding('strokeDashArray', 'from', v => lineStyle(v, 'strokeDashArray')), ), objGo( go.Shape, // 箭头 { toArrow: 'Standard' }, new go.Binding('stroke', 'from', v => lineStyle(v, 'stroke')), new go.Binding('fill', 'from', v => lineStyle(v, 'stroke')), ), objGo( go.Panel, 'Auto', objGo( go.Shape, // 标签背景,在边缘变得透明 // { fill: 'transparent' }, new go.Binding('fill', 'lineDetail', v => lineTextStyle(v)), new go.Binding('stroke', 'lineDetail', v => lineTextStyle(v)), ), objGo( go.TextBlock, { textAlign: 'center', font: '10pt helvetica, arial, Microsoft YaHei', stroke: '#555555', margin: 4, }, new go.Binding('text', 'lineDetail', v => lineText(v)), ), ), // { // // 处理双击 // doubleClick(e, node) { // addLineMsg(e, node); // }, // }, ); // 初始化流程的节点数组 diagram.model = objGo(go.GraphLinksModel, { linkFromPortIdProperty: 'fromPort', // 所需信息: linkToPortIdProperty: 'toPort', // 标识数据属性名称 nodeDataArray: currentFlowData.Nodes, linkDataArray: currentFlowData.Lines, }); }; // 线的样式 const lineStyle = (v, styleName) => { const linemsg = diagram.model.findNodeDataForKey(v); switch (styleName) { case 'strokeDashArray': if (linemsg.NodeType === '20') { return [6, 3]; } return null; case 'stroke': return '#1685FF'; default: return null; } }; // 线上文案样式 const lineTextStyle = v => { let obj = JSON.parse(v); let nodeData = diagram.model.findNodeDataForKey(obj.from); if (nodeData.NodeType === '20' || nodeData.NodeType === '21') { // if(nodeData.) if (nodeData.RuleList.some(ele => ele.NextNodeId === obj.to)) { return '#EFF8FA'; } return 'transparent'; } return 'transparent'; }; // 线上的文案 const lineText = v => { let obj = JSON.parse(v); let nodeData = diagram.model.findNodeDataForKey(obj.from); if (nodeData.NodeType === '20' || nodeData.NodeType === '21') { return nodeData.RuleList.find(ele => ele.NextNodeId === obj.to).RuleName; } return ''; }; // 是否显示端口 const showSmallPorts = (node, show) => { node.ports.each(port => { if (port.portId !== '') { // 不要更改默认端口,这是大形状 port.fill = show ? 'rgba(5,135,224,.3)' : null; } }); }; // 创建节点端口 const makePort = (name, spot) => // 端口基本上只是一个小的透明 方块 objGo( go.Shape, 'Circle', { fill: null, // 默认情况下不可见; 由 showSmallPorts 设置为半透明灰色,定义如下 stroke: null, desiredSize: new go.Size(8, 8), alignment: spot, // 对齐主要形状上的端口 alignmentFocus: spot, // 就在形状里面 portId: name, // 将此对象声明为“端口” fromSpot: spot, toSpot: spot, // 声明链接可以在此端口连接的位置 cursor: 'pointer', // 显示不同的光标以指示潜在的链接点 }, new go.Binding('fromLinkable', 'NodeType', v => v !== '2'), // 是否允许用户绘制的链接到这里 new go.Binding('toLinkable', 'NodeType', v => v !== '1'), // 声明用户是否可以从这里绘制链接 ); // 节点盒子样式 const nodeBoxStyle = (atr, classname) => { switch (atr) { case 'width': return new go.Binding('width', 'NodeType', v => { switch (v) { case '1': return 140; case '2': return 140; case '0': return 220; case '4': return 220; case '20': return 60; case '21': return 60; case '22': return 60; case '30': return 220; default: return null; } }); case 'height': return new go.Binding('height', 'NodeType', v => { switch (v) { case '1': return 140; case '2': return 140; case '0': return 120; case '4': return 120; case '20': return 60; case '21': return 60; case '22': return 60; case '30': return 120; default: return null; } }); case 'stroke': return new go.Binding('stroke', 'NodeType', v => { switch (v) { case '1': return classname === 'roleStyle' ? '#BCBCBC' : '#1685FF'; case '2': return classname === 'roleStyle' ? '#BCBCBC' : '#51C21A'; case '0': return classname === 'roleStyle' ? '#BCBCBC' : '#1685FF'; case '30': return classname === 'roleStyle' ? '#BCBCBC' : '#9850F6'; default: return null; } }); default: return null; } }; const findAfterNode = startNode => { // let nodeList = new Map([]); startNode.findNodesOutOf().each(node => { if (!afterNodes.current.has(node.data.NodeName)) { if (['1', '0', '2'].includes(node.data.NodeType)) { afterNodes.current.set(node.data.NodeName, node.data.TableName); } findAfterNode(node); } }); }; // 双击节点 const handlerDC = (e, node) => { currentNode.current = node.data; // 找到节点后得除去网关跟子流程的所有节点 afterNodes.current = new Map([]); findAfterNode(node); console.log(Object.fromEntries(afterNodes.current)); setModalType('edit'); setVisible(true); setNodeKey(node.part.data.key); setEditMsg(node.part.data); }; // 双击线 const addLineMsg = (e, node) => { setLineKey(node.part.data.LineKey); setLineMsg(node.part.data); setLineVisible(true); }; const copyNode = e => { // diagram.commandHandler.canSelectAll(); console.log( diagram.makeImageData({ background: 'rgb(239, 248, 250)', maxSize: new go.Size(1260, 500), }), 'fasdfsad', ); }; const nodeCallBack = () => { SaveWorkFlowImage({ flowName: flowData.flowName, base64Data: diagram.makeImageData({ background: 'rgb(239, 248, 250)', maxSize: new go.Size(1260, 500), }), }).then(response => { if (response.code === 0) { FlowNodeSave({ PreviewImage: response.data, CreateUser: sessionStorage.getItem('userName'), flowID, ...currentNode.current, CarbonCopyPeopleList: currentNode.current.CarbonCopyPeopleList.map(item => Number(item.value), ), }).then(res => { if (res.code === 0) { diagram.model.setDataProperty( currentNode.current, 'FlowTimerList', res.data.FlowTimerList, ); diagram.model.setDataProperty(currentNode.current, 'ActivityId', res.data.ActivityId); diagram.model.setDataProperty( currentNode.current, 'FlowNodeExtendId', res.data.FlowNodeExtendId, ); message.success('保存成功'); } else { message.error(res.msg); } }); } }); }; // 关闭时进行数据比对看数据是否改变 const leaveTip = () => { let diagramObj = JSON.parse(diagram.model.toJson()); let stageJson = { Nodes: diagramObj.nodeDataArray, Lines: diagramObj.linkDataArray, }; if (JSON.stringify(stageJson.Nodes) === JSON.stringify(initFlowData.Nodes)) { setShowLeaveTip(false); // leaveCallBack(false); } else { // leaveCallBack(true); setShowLeaveTip(true); } }; // 线配置回调函数 const lineCallBack = obj => { let node = diagram.model.findLinkDataForKey(LineKey); node.text = obj.text; diagram.model.updateTargetBindings(node); // 关闭时进行数据比对看数据是否改变 leaveTip(); setLineVisible(false); }; // 获取保存后的流程数据 const getFlowData = () => { GetFlowNode({ flowID }).then(res => { if (res.code === 0) { // 保存后离开不用提醒要修改数据了 setShowLeaveTip(false); // leaveCallBack(false); setCurrentFlowData(JSON.parse(JSON.stringify(res.data))); } else { message.error(res.msg); } }); }; const isRepeat = (arr, key) => { let obj = {}; for (let i = 0; i < arr.length; i++) { if (obj[arr[i][key]]) { return false; } obj[arr[i][key]] = arr[i]; } return obj; }; // 节点数据改边 const nodeChage = (key, value) => { console.log(key, value); setShowLeaveTip(true); let obj = JSON.parse(JSON.stringify(currentNode.current)); obj[key] = value; const nodeDetail = JSON.stringify(obj); diagram.model.setDataProperty(currentNode.current, key, value); if (key === 'roleList') { diagram.model.setDataProperty(currentNode.current, 'nodeDetail', nodeDetail); } if (key === 'FlowTimerList') { const list = value.map(item => item.EndNode); limitFinshNodes.current = new Set(list); setFlag(flag + 1); // limitFinshNodes; } if (key === 'TableName') { setFlag(flag + 1); } diagram.rebuildParts(); // leaveCallBack(true); }; // 保存流程 const saveFlow = () => { let diagramObj = JSON.parse(diagram.model.toJson()); // let list = isRepeat(diagramObj.nodeDataArray, 'SerialNo'); // if (!list) { // notification.error({ // message: '提示', // duration: 3, // description: '请检查序号是否重复', // }); // return; // } let list = new Set([]); diagramObj.nodeDataArray.forEach(item => { if ((item.NodeType === '20' || item.NodeType === '21') && item.RuleList) { item.RuleList.forEach(ele => { if (!ele.RuleName) { list.add(item.NodeName); return; } if (!ele.NextNodeId && ele.NextNodeId !== 0) { list.add(item.NodeName); return; } if (!ele.RuleContent) { list.add(item.NodeName); } }); } const newListLength = new Set(item.RuleList.map(ele => ele.NextNodeId)).size; if (item.RuleList.length > newListLength) { list.add(item.NodeName); return; } item.CarbonCopyPeopleList = item.CarbonCopyPeopleList.map(ele => Number(ele.value)); }); if ([...list].length > 0) { list.forEach(item => { message.error(`请检查${item}规则配置`); }); return; } setButtonLoading(true); SaveWorkFlowImage({ flowName: flowData.flowName, base64Data: diagram.makeImageData({ background: 'rgb(239, 248, 250)', maxSize: new go.Size(1260, 500), }), }).then(val => { if (val.code === 0) { SaveNodeChange({ FlowId: flowID, // DeleteNodes, CreateUser: sessionStorage.getItem('userName'), PreviewImage: val.data, DeleteLines, Lines: diagramObj.linkDataArray, Nodes: diagramObj.nodeDataArray, }) .then(res => { setButtonLoading(false); if (res.code === 0) { setDeleteNodes([]); setDeleteLines([]); setAddNodes([]); setDeleteNode(''); setDeleteLine(''); getFlowData(); message.success('保存成功'); } else { message.error(res.msg); } }) .catch(() => { setButtonLoading(false); message.error('网络异常请稍后重试'); }); } }); }; const treeChange = newValue => { setSelectValue(newValue); }; const mapAppTree = org => { const haveChildren = Array.isArray(org.children) && org.children.length > 0; let value; let text; if (org.name) { value = org.name; text = org.name; } if (org.Code) { value = org.Code; text = org.FlowName; } return ( <TreeNode value={value} title={text} key={value} disabled={org.name}> {haveChildren ? org.children.map(item => mapAppTree(item)) : null} </TreeNode> ); }; return ( <> <Prompt message="编辑的内容还未保存,确定要离开该页面吗?" when={showLeaveTip} /> <div className={styles.control}> <div className={styles.nodeList}> <div id="myPaletteNode" className={styles.myPaletteDiv} /> {/* <div className={styles.lineBox} /> */} <div id="myPaletteGateway" className={styles.myPaletteDiv} /> <div id="myPaletteSubprocess" className={styles.myPaletteSubprocess} /> </div> <div className={styles.buttonList}> {/* <Button type="link" onClick={() => { window.open( 'https://www.yuque.com/docs/share/da224db9-b8d1-49d2-838f-a23fcd15f0da?#%20%E3%80%8A%E6%B5%81%E7%A8%8B%E8%AE%BE%E8%AE%A1%E3%80%8B', ); }} > 说明文档 </Button> */} {/* <Button type="link" onClick={() => copyNode()}> 复制 </Button> */} {/* <Button type="link" onClick={() => copyNode()}> <TreeSelect value={selectValue} showSearch style={{ width: '200px' }} treeNodeFilterProp="title" dropdownStyle={{ maxHeight: 400, overflow: 'auto' }} placeholder="请选择流程" treeDefaultExpandAll onChange={treeChange} treeIcon > {flowTree ? ( flowTree.map(item => mapAppTree(item)) ) : ( <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> )} </TreeSelect> </Button> */} <Button onClick={() => history.push({ pathname: '/biz/workflow/center', state: { activeKey, scrollTop, }, }) } > 返回 </Button> <Button type="primary" onClick={() => saveFlow()} loading={buttonLoading}> 发布 </Button> </div> </div> <div className={styles.chartBox}> <div id="myOverviewDiv" className={styles.myOverviewDiv} /> <div className={styles.flowName}>{flowData.flowName}</div> <Spin spinning={chartLoading}> <div id="myDiagramDiv" className={styles.myDiagramDiv} style={{ backgroundColor: '#EFF8FA' }} /> </Spin> <NodeModal flowID={flowID} visible={visible} editMsg={editMsg} modalType={modalType} nodeChage={nodeChage} currentNode={currentNode.current} limitFinshNodes={[...limitFinshNodes.current]} afterNodes={Object.fromEntries(afterNodes.current)} handleCancel={() => setVisible(false)} onSubumit={obj => nodeCallBack(obj)} flowData={diagram ? JSON.parse(diagram.model.toJson()) : {}} /> </div> <LineModal visible={lineVisible} lineMsg={lineMsg} handleCancel={() => setLineVisible(false)} onSubumit={obj => lineCallBack(obj)} /> </> ); }; export default FlowChart;