using Microsoft.AspNetCore.Components; using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace wispro.sp.share.Utility { public class shapeNode { public int InCount { get; set; } public int OutCount { get; set; } public double x { get; set; } public double y { get; set; } public double width { get; set; } public double height { get; set; } /// /// 形状类型 /// 0:圆 /// 1:矩形 /// 3: 虚拟 /// public int Type { get; set; } public int Level { get; set; } = 1; public dynamic NodeObject { get; set; } public List Childrens { get; set; } public List Parents { get; set; } public string FillColor { get; set; } = "white"; } public class FlowChartUtility { public double TitleHeight { get; set; } = 100; public double ChartWidth { get; set; } = 1200; public double ChartHeight { get; set; } = 900; public double rectWidth { get; set; } = 150; public double rectHeight { get; set; } = 60; public double initRadius { get; set; } = 20; public double EndRadius { get; set; } = 20; public double hSeparation { get; set; } = 40; public string StepShapeColor { get; set; } = "green"; public string EndShapColor { get; set; } = "gray"; int rectFontSize { get; set; } = 18; public List Steps { get; set; } public List Transfers { get; set; } public List Actions { get; set; } public entity.workflowDefine.Workflow workflow { get; set; } private List shapeTrees = null; private shapeNode FindNode(int stepId, out int Level, List lstNodes) { Level = 1; foreach (var sNode in lstNodes) { if (sNode.NodeObject is entity.workflowDefine.Step && sNode.NodeObject.Id == stepId) { return sNode; } else { if (sNode.Childrens != null) { Level += 1; var retObj = FindNode(stepId, out Level, sNode.Childrens); if (retObj != null) { return retObj; } } } } return null; } private void GetShapeLevelNodes(Dictionary> levelNodes, List TreeNodes) { if (TreeNodes != null) { foreach (var sNode in TreeNodes) { List nodes = new List(); if (levelNodes.ContainsKey(sNode.Level)) { nodes = levelNodes[sNode.Level]; nodes.Add(sNode); } else { nodes.Add(sNode); levelNodes.Add(sNode.Level, nodes); } GetShapeLevelNodes(levelNodes, sNode.Childrens); } } } public shapeNode startNode; public shapeNode InitShape; public shapeNode endNode; public Dictionary> LevelNodes = new Dictionary>(); private void initShapeTree() { shapeTrees = new List(); LevelNodes = new Dictionary>(); #region 添加开始节点 startNode = new shapeNode() { //NodeObject = workflow.InitAction, InCount = 0, OutCount = 0, width = 2 * initRadius, height = 2 * initRadius, Level = 0 }; #endregion #region 添加初始化Action节点 InitShape = new shapeNode() { NodeObject = workflow.InitAction, InCount = 0, OutCount = 0, Type = 1, Level = 1, FillColor = StepShapeColor }; startNode.Childrens = new List(); startNode.Childrens.Add(InitShape); InitShape.Parents = new List(); InitShape.Parents.Add(startNode); shapeTrees.Add(startNode); #endregion #region 将步骤对象生成形状Node并添加到列表中 if (Steps != null) { foreach (var step in Steps) { var temNode = new shapeNode() { NodeObject = step, InCount = 0, OutCount = 0, Type = 1, FillColor = StepShapeColor }; if (workflow.EndStepId == step.Id) { endNode = new shapeNode() { NodeObject = step, InCount = 0, OutCount = 0, height = 2 * EndRadius, width = 2 * EndRadius, FillColor = EndShapColor }; temNode = endNode; } shapeTrees.Add(temNode); } } #endregion #region 遍历转移条件,生成流程树 if (Transfers != null && Transfers.Count > 0) { foreach (var transfer in Transfers) { var FromNode = InitShape; int FromLevel = 0; if (transfer.StepId != null) { FromNode = FindNode(transfer.StepId.Value, out FromLevel, shapeTrees); } int ToLevel = 0; var ToNode = FindNode(transfer.nextStepId, out ToLevel, shapeTrees); if (FromNode.Childrens == null) { FromNode.Childrens = new List(); } FromNode.Childrens.Add(ToNode); if (ToNode.Parents == null) { ToNode.Parents = new List(); } ToNode.Parents.Add(FromNode); FromNode.OutCount += 1; ToNode.InCount += 1; if (FromNode.Level >= ToLevel) { ToNode.Level = FromNode.Level + 1; } if (shapeTrees.Contains(ToNode)) { shapeTrees.Remove(ToNode); } } if (endNode.Parents != null && endNode.Parents[0].NodeObject is entity.workflowDefine.Step) { endNode.Level = endNode.Parents[0].Level + 1; } } else { InitShape.Childrens = new List(); InitShape.Childrens.Add(endNode); InitShape.OutCount += 1; endNode.Parents = new List(); endNode.Parents.Add(InitShape); endNode.InCount += 1; endNode.Level = InitShape.Level + 1; shapeTrees.Remove(endNode); //Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(endNode)); } #endregion GetShapeLevelNodes(LevelNodes, shapeTrees); #region 添加跨层连接的中间层的虚拟节点 List AddNodes = new List(); foreach (int level in LevelNodes.Keys) { foreach (var temNode in LevelNodes[level]) { if (temNode.Childrens != null) { foreach (var temChildrenNode in temNode.Childrens) { if ((temChildrenNode.Level - temNode.Level) > 1) { dynamic temAddInfo = new ExpandoObject(); temAddInfo.Parent = temNode; temAddInfo.Children = temChildrenNode; temAddInfo.Start = temNode.Level; temAddInfo.End = temChildrenNode.Level; AddNodes.Add(temAddInfo); } } } } } foreach (dynamic dynObj in AddNodes) { shapeNode parentNode = dynObj.Parent; shapeNode endNode = dynObj.Children; for (int i = dynObj.Start + 1; i < dynObj.End; i++) { var xnNode = new shapeNode() { Type = 3, Parents = new List(), Childrens = new List(), Level = i }; parentNode.Childrens.Add(xnNode); xnNode.Parents.Add(parentNode); parentNode.Childrens.Remove(endNode); parentNode.Childrens.Add(xnNode); endNode.Parents.Remove(parentNode); endNode.Parents.Add(xnNode); parentNode = xnNode; LevelNodes[xnNode.Level].Add(xnNode); } } #endregion int MaxNodeLevel = 0; int NodeCount = 0; foreach (int level in LevelNodes.Keys) { if (level > 1) { int temLevelCount = 0; foreach (var temNode in LevelNodes[level]) { int graterInOrOut = (temNode.OutCount > temNode.InCount) ? temNode.OutCount : temNode.InCount; if (graterInOrOut <= 1) { temLevelCount += 1; } else { temLevelCount += graterInOrOut; } } if (temLevelCount > MaxNodeLevel) { MaxNodeLevel = level; NodeCount = temLevelCount; } } } #region 每层节点排序 for (var i = 1; i < LevelNodes.Count; ++i) { var top_layer = LevelNodes[i - 1]; LevelNodes[i] = LevelNodes[i].OrderBy(node => { if (node.Parents == null) { return 0; } var connected_nodes = node.Parents .Where(l => l.Level == (i - 1)).ToList(); if (!connected_nodes.Any()) { return 0; } var average_index = connected_nodes.Select(cn => { var i = top_layer.IndexOf(cn); return i; }).Average(); return average_index; }).ToList(); } if (LevelNodes.Count > 1) { LevelNodes[0] = LevelNodes[0].OrderBy(node => { var connected_nodes = node.Childrens .Where(l => l.Level == 1) .ToList(); if (!connected_nodes.Any()) { return 0; } var average_index = connected_nodes.Select(cn => { var i = LevelNodes[1].IndexOf(cn); return i; }).Average(); return average_index; }).ToList(); } #endregion ArrangeNodesInRows(LevelNodes); } private void ArrangeNodesInRows(Dictionary> LevelNodes) { double preBotton = TitleHeight; for (int iLevel = 0; iLevel < LevelNodes.Count; iLevel++) { int iCount = 0; foreach (var node in LevelNodes[iLevel]) { int max = (node.InCount > node.OutCount) ? node.InCount : node.OutCount; if (max == 0) { max = 1; } iCount += max; } double onNodeWidth = ChartWidth / (iCount + 1); iCount = 0; double maxHeight = 0; foreach (var node in LevelNodes[iLevel]) { int max = (node.InCount > node.OutCount) ? node.InCount : node.OutCount; if (max == 0) { max = 1; } node.x = onNodeWidth * (iCount + 1) + (max - 1) * onNodeWidth / 2; if (node.Type == 0) { node.width = 2 * initRadius; node.height = 2 * initRadius; } else { node.width = rectWidth; node.height = rectHeight; } iCount += max; if (node.height > maxHeight) { maxHeight = node.height; } } foreach (var node in LevelNodes[iLevel]) { node.y = preBotton + hSeparation + maxHeight / 2; } preBotton = preBotton + hSeparation + maxHeight; } } public dynamic GetLineParater(entity.workflowDefine.TrasferCondition trasferCondition) { int level = 0; var startNode = InitShape; if (trasferCondition.StepId != null) { startNode = FindNode(trasferCondition.StepId.Value, out level, shapeTrees); } var endNode = FindNode(trasferCondition.nextStepId, out level, shapeTrees); dynamic ret = new ExpandoObject(); ret.x1 = startNode.x; ret.y1 = startNode.y + startNode.height / 2; ret.x2 = endNode.x; ret.y2 = endNode.y - endNode.height / 2; double x3 = 0.0; double y3 = 0.0; GetArrorEndPoint(ret, out x3, out y3); ret.x2 = x3; ret.y2 = y3; return ret; } public void GetArrorEndPoint(dynamic ret, out double x3, out double y3) { if (ret.x1 == ret.x2) { x3 = ret.x1; if (ret.y2 > ret.y1) { y3 = ret.y2 - 8; } else { y3 = ret.y2 + 8; } } else { if (ret.y1 == ret.y2) { y3 = ret.y1; if (ret.x1 < ret.x2) { x3 = ret.x2 - 8; } else { x3 = ret.x2 + 8; } } else { double k = (ret.y1 - ret.y2) / (ret.x1 - ret.x2); double b = ret.y2 - k * ret.x2; double a = k * k + 1; double B = 2 * k * (b - ret.y2) - 2 * ret.x2; double c = (b - ret.y2) * (b - ret.y2) + ret.x2 * ret.x2 - 100; double x3_1 = (Math.Sqrt(B * B - 4 * a * c) - B) / (2 * a); double x3_2 = ((Math.Sqrt(B * B - 4 * a * c) + B) / (2 * a)) * -1; if (ret.x1 < ret.x2) { x3 = (x3_1 < x3_2) ? x3_1 : x3_2; } else { x3 = (x3_1 < x3_2) ? x3_2 : x3_1; } y3 = k * x3 + b; } } } public dynamic GetEndStepLine() { var startNode = InitShape; if (endNode.Parents != null) { startNode = endNode.Parents[0]; } dynamic ret = new ExpandoObject(); ret.x1 = startNode.x; ret.y1 = startNode.y + startNode.height / 2; ret.x2 = endNode.x; ret.y2 = endNode.y - endNode.height / 2; double x3 = 0.0; double y3 = 0.0; GetArrorEndPoint(ret, out x3, out y3); ret.x2 = x3; ret.y2 = y3; return ret; } public dynamic GetStartInitLine() { dynamic ret = new ExpandoObject(); ret.x1 = this.startNode.x; ret.y1 = this.startNode.y + startNode.height / 2; ret.x2 = InitShape.x; ret.y2 = InitShape.y - InitShape.height / 2; double x3 = 0.0; double y3 = 0.0; GetArrorEndPoint(ret, out x3, out y3); ret.x2 = x3; ret.y2 = y3; return ret; } public void Refresh() { //System.Text.Json.JsonSerializer.Serialize(Actions); initShapeTree(); } void InitAction() { } #region 单击/双击事件 public EventCallback OnClickStep { get; set; } public EventCallback OnClickAction { get; set; } public EventCallback OnClickTransfer { get; set; } public EventCallback OnDoubleClickStep { get; set; } [Parameter] public EventCallback OnDoubleClickAction { get; set; } [Parameter] public EventCallback OnDoubleClickTransfer { get; set; } public shapeNode SelectedShape { get; set; } void ClickNode(shapeNode node) { SelectedShape = node; if (node.NodeObject is entity.workflowDefine.Step) { if (OnClickStep.HasDelegate) { OnClickStep.InvokeAsync((entity.workflowDefine.Step)node.NodeObject); } } if (node.NodeObject is entity.workflowDefine.Action) { if (OnClickAction.HasDelegate) { OnClickAction.InvokeAsync((entity.workflowDefine.Action)node.NodeObject); } } } void DoubleClickNode(shapeNode node) { SelectedShape = node; if (node.NodeObject is entity.workflowDefine.Step) { if (OnDoubleClickStep.HasDelegate) { OnDoubleClickStep.InvokeAsync((entity.workflowDefine.Step)node.NodeObject); } } if (node.NodeObject is entity.workflowDefine.Action) { if (OnDoubleClickAction.HasDelegate) { OnDoubleClickAction.InvokeAsync((entity.workflowDefine.Action)node.NodeObject); } } } void ClickTrasfer(entity.workflowDefine.TrasferCondition trasferCondition) { SelectedShape = null; if (OnClickTransfer.HasDelegate) { OnClickTransfer.InvokeAsync(trasferCondition); } } void DoubleClickTrasfer(entity.workflowDefine.TrasferCondition trasferCondition) { SelectedShape = null; if (OnDoubleClickTransfer.HasDelegate) { OnDoubleClickTransfer.InvokeAsync(trasferCondition); } } #endregion public string GetSvgString() { initShapeTree(); string strReturn = $""; strReturn = strReturn + "\r\n" + "\t\r\n" + "\t\t\r\n" + "\r\n" + "\r\n"; if (shapeTrees != null) { strReturn += $"{workflow.Name}\r\n"; dynamic startLine = GetStartInitLine(); strReturn += $"\r\n"; for (int iLevel = 0; iLevel < LevelNodes.Count; iLevel++) { foreach (var node in LevelNodes[iLevel]) { if (node.Type == 0) { strReturn += $"\r\n"; strReturn += "" + $"\r\n"; if (node == startNode) { strReturn += "开始"; } else { if (node == endNode) { strReturn += "结束"; } else { if (node.NodeObject is entity.workflowDefine.Step) { strReturn += ((entity.workflowDefine.Step)node.NodeObject).Name; } } } strReturn += "\r\n\r\n"; } if (node.Type == 1) { strReturn += $"\r\n"; strReturn += $" DoubleClickNode(node)\" onclick=\"()=>ClickNode(node)\">\r\n"; strReturn += $"\r\n"; if (node.NodeObject is entity.workflowDefine.Action) { strReturn += ((workflow.InitAction == null || string.IsNullOrEmpty(workflow.InitAction.Name)) ? $"启动{workflow.Name}" : workflow.InitAction.Name) + "\r\n"; } else { strReturn += (((entity.workflowDefine.Step)node.NodeObject).Name) + "\r\n"; } strReturn += "\r\n\r\n"; if (SelectedShape == node) { strReturn += $"\r\n"; strReturn += $"\r\n"; strReturn += $"\r\n"; strReturn += $"\r\n"; } } } } if (Transfers.Count > 0) { foreach (var t in Transfers) { dynamic ret = GetLineParater(t); strReturn += $"\r\n"; strReturn += $"\r\n"; strReturn += $" DoubleClickTrasfer(t)\" onclick=\"()=>ClickTrasfer(t)\">\r\n"; strReturn += $"c\r\n"; strReturn += "\r\n"; } } else { dynamic ret = GetEndStepLine(); strReturn += $"\r\n"; } //dynamic endLine = GetEndStepLine(); //strReturn += $"\r\n"; } return strReturn + ""; } } }