using Microsoft.AspNetCore.Components; using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Threading.Tasks; namespace wispro.sp.winClient { public partial class FlowChart { [Parameter] public double TitleHeight { get; set; } = 100; [Parameter] public double ChartWidth { get; set; } = 1200; [Parameter] public double ChartHeight { get; set; } = 900; [Parameter] public double rectWidth { get; set; } = 150; [Parameter] public double rectHeight { get; set; } = 60; [Parameter] public double initRadius { get; set; } = 25; [Parameter] public double EndRadius { get; set; } = 25; [Parameter] public double hSeparation { get; set; } = 40; 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; } internal 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; } } 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); } } } shapeNode startNode; shapeNode InitShape; shapeNode endNode; Dictionary> LevelNodes = new Dictionary>(); private void initShapeTree() { shapeTrees = new List(); LevelNodes = new Dictionary>(); startNode = new shapeNode() { //NodeObject = workflow.InitAction, InCount = 0, OutCount = 0, width = 2 * initRadius, height = 2 * initRadius, Level = 0 }; InitShape = new shapeNode() { NodeObject = workflow.InitAction, InCount = 0, OutCount = 0, Type = 1, Level = 1 }; startNode.Childrens = new List(); startNode.Childrens.Add(InitShape); InitShape.Parents = new List(); InitShape.Parents.Add(startNode); endNode = new shapeNode() { InCount = 0, OutCount = 0, height = 2 * EndRadius, width = 2 * EndRadius }; shapeTrees.Add(startNode); #region 将步骤对象生成形状Node并添加到列表中 if (Steps == null || Steps.Count == 0 || Steps.FirstOrDefault(s => s.Id == workflow.EndStepId) == null) { 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; } foreach (var step in Steps) { var temNode = new shapeNode() { NodeObject = step, InCount = 0, OutCount = 0, Type = 1 }; if (workflow.EndStepId == step.Id) { temNode.Childrens = new List(); temNode.Childrens.Add(endNode); endNode.Parents = new List(); endNode.Parents.Add(temNode); } shapeTrees.Add(temNode); } #endregion #region 遍历转移条件,生成流程树 if (Transfers != null) { 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); } } endNode.Level = endNode.Parents[0].Level + 1; } #endregion GetShapeLevelNodes(LevelNodes, shapeTrees); #region 添加跨层连接的中间层的虚拟节点 //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) // { // temNode.Childrens.Remove(temChildrenNode); // temChildrenNode.Parents.Remove(temNode); // var parentNode = temNode; // for (int iLevel = temNode.Level + 1; iLevel < temChildrenNode.Level; iLevel++) // { // var xnNode = new shapeNode() { Type = 3, Parents = new List(), Childrens = new List() }; // parentNode.Childrens.Add(xnNode); // xnNode.Parents = new List(); // xnNode.Parents.Add(parentNode); // parentNode = xnNode; // } // parentNode.Childrens.Add(temChildrenNode); // temChildrenNode.Parents.Add(parentNode); // } // } // } // } //} #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 每层节点排序 // 2.1. sort out each layer by looking at where it connects from for (var i = 1; i < LevelNodes.Count; ++i) { var top_layer = LevelNodes[i - 1]; LevelNodes[i] = LevelNodes[i].OrderBy(node => { // calculate average position based on connected nodes in top layer 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(); } // 2.2. now that all but the first layer are layed out, let's deal with the first if (LevelNodes.Count > 1) { LevelNodes[0] = LevelNodes[0].OrderBy(node => { // calculate average position based on connected nodes in top layer 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; return ret; } public dynamic GetEndStepLine() { var 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; return ret; } public void Refresh() { System.Text.Json.JsonSerializer.Serialize(Actions); initShapeTree(); } void ClickStep(entity.workflowDefine.Step step) { } void ClickTrasfer(entity.workflowDefine.TrasferCondition trasferCondition) { } void InitAction() { } void ClickNode(shapeNode node) { } } }