ソースを参照

流程组件添加点击事件

luocaiyang 3 年 前
コミット
a4fc51af5c

+ 64 - 56
wispro.sp.web/Components/FlowChart.razor

@@ -1,86 +1,94 @@
 
     @if (shapeTrees != null)
     {
-    <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="@ChartWidth" height="@ChartHeight">
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="@ChartWidth" height="@ChartHeight">
+    <defs>
+        <marker id="idArrow"
+                viewBox="0 0 20 20" refX="0" refY="10"
+                markerUnits="strokeWidth" markerWidth="5" markerHeight="10"
+                orient="auto">
+            <path d="M 0 0 L 20 10 L 0 20 z" fill="black" stroke="black" />
+        </marker>
 
+    </defs>
 
-        <text x="@(ChartWidth/2)" y="@(TitleHeight/2)" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="30" font-weight="800">@workflow.Name</text>
+    <text x="@(ChartWidth/2)" y="@(TitleHeight/2)" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="30" font-weight="800">@workflow.Name</text>
 
-        <line x1="@startNode.x" y1="@(startNode.y + startNode.width / 2) " x2="@(InitShape.x)" y2="@(InitShape.y - InitShape.height / 2)" stroke="black" stroke-width="2" />
+    <line x1="@startNode.x" y1="@(startNode.y + startNode.width / 2) " x2="@(InitShape.x)" y2="@(InitShape.y - InitShape.height / 2)" stroke="black" stroke-width="2" marker-end="url(#idArrow)" />
 
-        @for (int iLevel = 0; iLevel < LevelNodes.Count; iLevel++)
+    @for (int iLevel = 0; iLevel < LevelNodes.Count; iLevel++)
+    {
+        @foreach (var node in LevelNodes[iLevel])
         {
-            @foreach (var node in LevelNodes[iLevel])
+            if (node.Type == 0)
             {
-                if (node.Type == 0)
-                {
-                    <circle cx="@(node.x)" cy="@node.y" r="@(node.width / 2) " stroke="black" stroke-width="2" fill="white" />
-                    <a @onclick="() => ClickNode(node)">
-                        <text x="@(node.x)" y="@node.y" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="16">
-                            @if (node == startNode)
+                <circle cx="@(node.x)" cy="@node.y" r="@(node.width / 2) " stroke="black" stroke-width="2" fill="white" />
+                <a @onclick="() => ClickNode(node)">
+                    <text x="@(node.x)" y="@node.y" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="16">
+                        @if (node == startNode)
+                        {
+                            @("开始")
+                        }
+                        else
+                        {
+                            if (node == endNode)
                             {
-                                @("开始")
+                                @("结束")
                             }
                             else
                             {
-                                if (node == endNode)
-                                {
-                                    @("结束")
-                                }
-                                else
+                                if (node.NodeObject is entity.workflowDefine.Step)
                                 {
-                                    if (node.NodeObject is entity.workflowDefine.Step)
-                                    {
-                                        @(((entity.workflowDefine.Step)node.NodeObject).Name)
-                                    }
+                                    @(((entity.workflowDefine.Step)node.NodeObject).Name)
                                 }
                             }
-                        </text>
-                    </a>
-                }
+                        }
+                    </text>
+                </a>
+            }
 
-                if (node.Type == 1)
-                {
-                    <rect x="@(node.x - node.width / 2)" y="@(node.y - node.height / 2)" rx="10" ry="10" width="@node.width" height="@node.height" style="fill:green;stroke:black;stroke-width:3;opacity:0.5" />
-                    <a @onclick="() => ClickNode(node)">
-                        <text x="@node.x" y="@node.y" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="@rectFontSize" font-weight="700">
-                            @if (node == InitShape)
-                            {
-                                @((workflow.InitAction == null || string.IsNullOrEmpty(workflow.InitAction.Name)) ? $"启动{workflow.Name}" : workflow.InitAction.Name)
-                            }
-                            else
-                            {
-                                @(((entity.workflowDefine.Step)node.NodeObject).Name)
-                            }
-                        </text>
-                    </a>
-                }
+            if (node.Type == 1)
+            {
+                <rect x="@(node.x - node.width / 2)" y="@(node.y - node.height / 2)" rx="10" ry="10" width="@node.width" height="@node.height" style="fill:green;stroke:black;stroke-width:3;opacity:0.5" />
+                <a @onclick="() => ClickNode(node)">
+                    <text x="@node.x" y="@node.y" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="@rectFontSize" font-weight="700">
+                        @if (node.NodeObject is entity.workflowDefine.Action)
+                        {
+                            @((workflow.InitAction == null || string.IsNullOrEmpty(workflow.InitAction.Name)) ? $"启动{workflow.Name}" : workflow.InitAction.Name)
+                        }
+                        else
+                        {
+                            @(((entity.workflowDefine.Step)node.NodeObject).Name)
+                        }
+                    </text>
+                </a>
             }
         }
+    }
 
-        @foreach (var t in Transfers)
-        {
-            dynamic ret = GetLineParater(t);
-            <line x1="@ret.x1" y1="@ret.y1" x2="@ret.x2" y2="@ret.y2" stroke="black" stroke-width="2" />
+    @foreach (var t in Transfers)
+    {
+        dynamic ret = GetLineParater(t);
+        <line x1="@ret.x1" y1="@ret.y1" x2="@ret.x2" y2="@ret.y2" stroke="black" stroke-width="2" marker-end="url(#idArrow)" />
 
-            <circle cx="@((ret.x1 + ret.x2) / 2)" cy="@((ret.y1 + ret.y2) / 2)" r="5" stroke="black" stroke-width="1" fill="white" />
-            <a @onclick="() => ClickTrasfer(t)">
-                <text x="@((ret.x1 + ret.x2) / 2)" y="@((ret.y1 + ret.y2) / 2)" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="5">c</text>
-            </a>
+        <circle cx="@((ret.x1 + ret.x2) / 2)" cy="@((ret.y1 + ret.y2) / 2)" r="7" stroke="black" stroke-width="1.5" fill="white" />
+        <a @onclick="() => ClickTrasfer(t)">
+            <text x="@((ret.x1 + ret.x2) / 2)" y="@((ret.y1 + ret.y2) / 2)" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="12">c</text>
+        </a>
 
-        }
+    }
 
-        
-        @{ 
-            dynamic endLine = GetEndStepLine();
-            <line x1="@endLine.x1" y1="@endLine.y1" x2="@endLine.x2" y2="@endLine.y2" stroke="black" stroke-width="2" />
-        }
 
-        @*<circle cx="@(endNode.x)" cy="@endNode.y" r="@(endNode.width/2) " stroke="black" stroke-width="2" fill="gray" />
+    @{
+        dynamic endLine = GetEndStepLine();
+        <line x1="@endLine.x1" y1="@endLine.y1" x2="@endLine.x2" y2="@endLine.y2" stroke="black" stroke-width="2" marker-end="url(#idArrow)"/>
+    }
+
+    @*<circle cx="@(endNode.x)" cy="@endNode.y" r="@(endNode.width/2) " stroke="black" stroke-width="2" fill="gray" />
         <a>
             <text x="@(endNode.x)" y="@endNode.y" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="5">结束</text>
         </a>*@
-    </svg>
+</svg>
     }
     else
     {

+ 121 - 113
wispro.sp.web/Components/FlowChart.razor.cs

@@ -4,28 +4,42 @@ using System.Collections.Generic;
 using System.Dynamic;
 using System.Linq;
 using System.Threading.Tasks;
+using wispro.sp.entity.workflowDefine;
 
 namespace wispro.sp.web.Components
 {
     public partial class FlowChart
     {
-        double TitleHeight = 100;
-        double ChartWidth = 1400;
-        double ChartHeight = 1000;
-        double rectWidth = 150;
-        double rectHeight = 60;
-        double initRadius = 25;
-        double EndRadius = 25;
-        double hSeparation = 40;
+        [Parameter]
+        public double TitleHeight { get; set; } = 100;
 
-        int rectFontSize = 18;
 
+        [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;
 
-        private List<entity.workflowDefine.Step> Steps { get; set; }
+        int rectFontSize { get; set; } = 18;
 
-        private List<entity.workflowDefine.TrasferCondition> Transfers { get; set; }
+        [Parameter]
+        public List<entity.workflowDefine.Step> Steps { get; set; }
 
-        private List<entity.workflowDefine.Action> Actions { get; set; }
+        [Parameter]
+        public List<entity.workflowDefine.TrasferCondition> Transfers { get; set; }
+
+        [Parameter]
+        public List<entity.workflowDefine.Action> Actions { get; set; }
 
         [Parameter]
         public entity.workflowDefine.Workflow workflow { get; set; }
@@ -65,9 +79,7 @@ namespace wispro.sp.web.Components
 
         private shapeNode FindNode(int stepId, out int Level, List<shapeNode> lstNodes)
         {
-            //Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(lstNodes));
-            Console.WriteLine(stepId.ToString());
-            Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(Steps));
+            
             Level = 1;
             foreach (var sNode in lstNodes)
             {
@@ -125,61 +137,8 @@ namespace wispro.sp.web.Components
 
         private void initShapeTree()
         {
-            #region Demo流程数据
-            workflow = new entity.workflowDefine.Workflow();
-            workflow.Name = "请假处理流程";
-            workflow.Id = 1;
-
-            workflow.InitAction = new entity.workflowDefine.Action();
-            workflow.InitAction.Name = "填写请教条";
-
-            Steps = new List<entity.workflowDefine.Step>();
-            var step1 = new entity.workflowDefine.Step();
-            step1.Name = "部门主管申核";
-            step1.Id = 1;
-            Steps.Add(step1);
-
-            var step2 = new entity.workflowDefine.Step();
-            step2.Name = "老板申核";
-            step2.Id = 2;
-            Steps.Add(step2);
-
-            var step3 = new entity.workflowDefine.Step();
-            step3.Name = "申核结果通知";
-            step3.Id = 3;
-            Steps.Add(step3);
-
-            var step4 = new entity.workflowDefine.Step();
-            step4.Name = "新步骤";
-            step4.Id = 4;
-            Steps.Add(step4);
-
-            workflow.EndStepId = 3;
-
-            Transfers = new List<entity.workflowDefine.TrasferCondition>();
-            var t = new entity.workflowDefine.TrasferCondition();
-            t.StepId = null;
-            t.nextStepId = 1;
-            Transfers.Add(t);
-
-            var t1 = new entity.workflowDefine.TrasferCondition();
-            t1.StepId = 1;
-            t1.nextStepId = 2;
-            Transfers.Add(t1);
-
-            var t2 = new entity.workflowDefine.TrasferCondition();
-            t2.StepId = 1;
-            t2.nextStepId = 3;
-            Transfers.Add(t2);
-
-            var t3 = new entity.workflowDefine.TrasferCondition();
-            t3.StepId = 2;
-            t3.nextStepId = 3;
-            Transfers.Add(t3);
-            #endregion
-
-
             shapeTrees = new List<shapeNode>();
+            LevelNodes = new Dictionary<int, List<shapeNode>>();
 
             startNode = new shapeNode()
             {
@@ -190,8 +149,6 @@ namespace wispro.sp.web.Components
                 height = 2 * initRadius,
                 Level = 0
             };
-
-
             InitShape = new shapeNode()
             {
                 NodeObject = workflow.InitAction,
@@ -220,64 +177,82 @@ namespace wispro.sp.web.Components
             //shapeTrees.Add(endNode);
 
             #region 将步骤对象生成形状Node并添加到列表中
-            foreach (var step in Steps)
+            if (Steps == null || Steps.Count == 0 || Steps.FirstOrDefault(s=>s.Id == workflow.EndStepId) == null)
             {
-                var temNode = new shapeNode() { NodeObject = step, InCount = 0, OutCount = 0, Type = 1 };
+                InitShape.Childrens = new List<shapeNode>();
+                InitShape.Childrens.Add(endNode);
+                InitShape.OutCount += 1;
+                endNode.Parents = new List<shapeNode>();
+                endNode.Parents.Add(InitShape);
+                endNode.InCount += 1;
+                endNode.Level = InitShape.Level + 1;
+            }
 
-                if (workflow.EndStepId == step.Id)
+            if (Steps != null)
+            {
+                foreach (var step in Steps)
                 {
-                    temNode.Childrens = new List<shapeNode>();
-                    temNode.Childrens.Add(endNode);
-                    endNode.Parents = new List<shapeNode>();
-                    endNode.Parents.Add(temNode);
-                }
+                    var temNode = new shapeNode() { NodeObject = step, InCount = 0, OutCount = 0, Type = 1 };
 
-                shapeTrees.Add(temNode);
+                    if (workflow.EndStepId == step.Id)
+                    {
+                        temNode.Childrens = new List<shapeNode>();
+                        temNode.Childrens.Add(endNode);
+                        endNode.Parents = new List<shapeNode>();
+                        endNode.Parents.Add(temNode);
+                    }
+
+                    shapeTrees.Add(temNode);
+                }
             }
+            
             #endregion
 
             #region 遍历转移条件,生成流程树
-            foreach (var transfer in Transfers)
+            if (Transfers != null)
             {
-                var FromNode = InitShape;
-                int FromLevel = 0;
-
-                if (transfer.StepId != null)
+                foreach (var transfer in Transfers)
                 {
-                    FromNode = FindNode(transfer.StepId.Value, out FromLevel, shapeTrees);
-                }
+                    var FromNode = InitShape;
+                    int FromLevel = 0;
 
-                int ToLevel = 0;
-                var ToNode = FindNode(transfer.nextStepId, out ToLevel, shapeTrees);
+                    if (transfer.StepId != null)
+                    {
+                        FromNode = FindNode(transfer.StepId.Value, out FromLevel, shapeTrees);
+                    }
 
-                if (FromNode.Childrens == null)
-                {
-                    FromNode.Childrens = new List<shapeNode>();
-                }
-                FromNode.Childrens.Add(ToNode);
+                    int ToLevel = 0;
+                    var ToNode = FindNode(transfer.nextStepId, out ToLevel, shapeTrees);
 
-                if (ToNode.Parents == null)
-                {
-                    ToNode.Parents = new List<shapeNode>();
-                }
-                ToNode.Parents.Add(FromNode);
+                    if (FromNode.Childrens == null)
+                    {
+                        FromNode.Childrens = new List<shapeNode>();
+                    }
+                    FromNode.Childrens.Add(ToNode);
 
-                FromNode.OutCount += 1;
-                ToNode.InCount += 1;
+                    if (ToNode.Parents == null)
+                    {
+                        ToNode.Parents = new List<shapeNode>();
+                    }
+                    ToNode.Parents.Add(FromNode);
 
-                if (FromNode.Level >= ToLevel)
-                {
-                    ToNode.Level = FromNode.Level + 1;
-                }
+                    FromNode.OutCount += 1;
+                    ToNode.InCount += 1;
 
-                if (shapeTrees.Contains(ToNode))
-                {
+                    if (FromNode.Level >= ToLevel)
+                    {
+                        ToNode.Level = FromNode.Level + 1;
+                    }
 
-                    shapeTrees.Remove(ToNode);
+                    if (shapeTrees.Contains(ToNode))
+                    {
+
+                        shapeTrees.Remove(ToNode);
+                    }
                 }
-            }
 
-            endNode.Level = endNode.Parents[0].Level + 1;
+                endNode.Level = endNode.Parents[0].Level + 1;
+            }
             #endregion
 
             
@@ -460,7 +435,6 @@ namespace wispro.sp.web.Components
 
 
         }
-
         
         public dynamic GetLineParater(entity.workflowDefine.TrasferCondition trasferCondition)
         {
@@ -500,7 +474,12 @@ namespace wispro.sp.web.Components
             initShapeTree();
         }
 
-
+        public void Refresh()
+        {
+            System.Text.Json.JsonSerializer.Serialize(Actions);
+            initShapeTree();
+            StateHasChanged();
+        }
 
         void ClickStep(entity.workflowDefine.Step step)
         {
@@ -509,7 +488,10 @@ namespace wispro.sp.web.Components
 
         void ClickTrasfer(entity.workflowDefine.TrasferCondition trasferCondition)
         {
-
+            if (OnClickTransfer.HasDelegate)
+            {
+                OnClickTransfer.InvokeAsync(trasferCondition);
+            }
         }
 
         void InitAction()
@@ -517,10 +499,36 @@ namespace wispro.sp.web.Components
 
         }
 
+        [Parameter]
+        public EventCallback<entity.workflowDefine.Step> OnClickStep { get; set; }
+
+        [Parameter]
+        public EventCallback<entity.workflowDefine.Action> OnClickAction{ get; set; }
+
+        [Parameter]
+        public EventCallback<entity.workflowDefine.TrasferCondition> OnClickTransfer { get; set; }
+
         void ClickNode(shapeNode 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);
+                }
+            }
 
         }
+
+        
     }
 
 }

+ 27 - 0
wispro.sp.web/Pages/Workflow/WorkflowDetail.razor

@@ -0,0 +1,27 @@
+@page "/Workflow/Detail"
+
+<PageContainer>
+    <Breadcrumb>
+        <Breadcrumb>
+            <BreadcrumbItem>
+                <a href="/Home"><Icon Type="home"></Icon></a>
+            </BreadcrumbItem>
+            <BreadcrumbItem>
+                <Icon Type="apartment" Theme="outline" /><span>流程管理</span>
+            </BreadcrumbItem>
+        </Breadcrumb>
+    </Breadcrumb>
+    <Content>
+        <Button Type="primary" Icon="plus" OnClick="AddNewStep" Style="float:right">添加新步骤</Button>
+    </Content>
+    <ChildContent>
+        <div style="height:800px;width:100%;overflow:auto;background:#FFFFFF;">
+            <wispro.sp.web.Components.FlowChart @ref="chart" 
+                            workflow="@workflow" Actions="@Actions" 
+                            Transfers="@Transfers" Steps="@Steps" 
+                            OnClickAction="OnClickAction"
+                            OnClickStep="OnClickStep"
+                            OnClickTransfer="OnClickTransfer"/>
+        </div>
+    </ChildContent>
+</PageContainer>

+ 112 - 0
wispro.sp.web/Pages/Workflow/WorkflowDetail.razor.cs

@@ -0,0 +1,112 @@
+using AntDesign;
+using Microsoft.AspNetCore.Components;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace wispro.sp.web.Pages.Workflow
+{
+    public partial class WorkflowDetail
+    {
+        Components.FlowChart chart;
+
+        entity.workflowDefine.Workflow workflow;
+        List<entity.workflowDefine.Step> Steps;
+        List<entity.workflowDefine.Action> Actions;
+        List<entity.workflowDefine.TrasferCondition> Transfers;
+
+        
+        [Inject] protected MessageService _msgService { get; set; }
+
+        protected override void OnInitialized()
+        {
+            base.OnInitialized();
+
+            #region Demo流程数据
+            workflow = new entity.workflowDefine.Workflow();
+            workflow.Name = "专案绩效流程";
+            workflow.Id = 1;
+            workflow.EndStepId = -1;
+
+            workflow.InitAction = new entity.workflowDefine.Action();
+            workflow.InitAction.Name = "填写请假条";
+
+            Steps = new List<entity.workflowDefine.Step>();
+            var step1 = new entity.workflowDefine.Step();
+            step1.Name = "部门主管申核";
+            step1.Id = 1;
+            Steps.Add(step1);
+
+            var step2 = new entity.workflowDefine.Step();
+            step2.Name = "老板申核";
+            step2.Id = 2;
+            Steps.Add(step2);
+
+            var step3 = new entity.workflowDefine.Step();
+            step3.Name = "申核结果通知";
+            step3.Id = 3;
+            Steps.Add(step3);
+
+            //var step4 = new entity.workflowDefine.Step();
+            //step4.Name = "新步骤";
+            //step4.Id = 4;
+            //Steps.Add(step4);
+
+            //var step5 = new entity.workflowDefine.Step();
+            //step5.Name = "新步骤";
+            //step5.Id = 5;
+            //Steps.Add(step5);
+
+
+            workflow.EndStepId = 3;
+
+            Transfers = new List<entity.workflowDefine.TrasferCondition>();
+            var t = new entity.workflowDefine.TrasferCondition();
+            t.StepId = null;
+            t.nextStepId = 1;
+            Transfers.Add(t);
+
+            var t1 = new entity.workflowDefine.TrasferCondition();
+            t1.StepId = 1;
+            t1.nextStepId = 2;
+            Transfers.Add(t1);
+
+            var t2 = new entity.workflowDefine.TrasferCondition();
+            t2.StepId = 1;
+            t2.nextStepId = 3;
+            Transfers.Add(t2);
+
+            var t3 = new entity.workflowDefine.TrasferCondition();
+            t3.StepId = 2;
+            t3.nextStepId = 3;
+            Transfers.Add(t3);
+            #endregion
+        }
+
+        void AddNewStep()
+        {
+            var newStep = new entity.workflowDefine.Step();
+            newStep.Name = "新步骤";
+            newStep.workflowId = workflow.Id;
+            Steps.Add(newStep);
+
+            chart.Refresh();
+        }
+
+        void OnClickStep(entity.workflowDefine.Step step)
+        {
+            _msgService.Info($"您点击了:【{step.Name}】步骤");
+        }
+
+        void OnClickAction(entity.workflowDefine.Action action)
+        {
+            _msgService.Info($"您点击了:【{action.Name}】操作");
+        }
+
+        void OnClickTransfer(entity.workflowDefine.TrasferCondition transfer)
+        {
+            _msgService.Info($"您点击了:从步骤【{transfer.StepId}】到步骤【{transfer.nextStepId}】的移转条件!");
+        }
+    }
+}

+ 83 - 0
wispro.sp.web/wwwroot/test.png

@@ -0,0 +1,83 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1400" height="1000"><!--!-->
+    <!--!--><defs>
+        <marker id="idArrow" viewBox="0 0 20 20" refX="0" refY="10" markerUnits="strokeWidth" markerWidth="5" markerHeight="10" orient="auto">
+            <path d="M 0 0 L 20 10 L 0 20 z" fill="black" stroke="black"></path>
+        </marker>
+
+    </defs>
+
+    <text x="700" y="50" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="30" font-weight="800">请假处理流程</text><!--!-->
+
+    <line x1="700" y1="190 " x2="466.6666666666667" y2="230" stroke="black" stroke-width="2" marker-end="url(#idArrow)"></line><!--!-->
+
+                <circle cx="700" cy="165" r="25 " stroke="black" stroke-width="2" fill="white"></circle><!--!-->
+                <a><!--!-->
+                    <text x="700" y="165" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="16"><!--!-->
+开始                    </text><!--!-->
+                </a><!--!-->
+                <rect x="391.6666666666667" y="230" rx="10" ry="10" width="150" height="60" style="fill:green;stroke:black;stroke-width:3;opacity:0.5"></rect><!--!-->
+                <a><!--!-->
+                    <text x="466.6666666666667" y="260" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="18" font-weight="700"><!--!-->
+填写请教条                    </text><!--!-->
+                </a><!--!-->
+                <rect x="858.3333333333334" y="230" rx="10" ry="10" width="150" height="60" style="fill:green;stroke:black;stroke-width:3;opacity:0.5"></rect><!--!-->
+                <a><!--!-->
+                    <text x="933.3333333333334" y="260" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="18" font-weight="700"><!--!-->
+新步骤                    </text><!--!-->
+                </a><!--!-->
+                <rect x="625" y="330" rx="10" ry="10" width="150" height="60" style="fill:green;stroke:black;stroke-width:3;opacity:0.5"></rect><!--!-->
+                <a><!--!-->
+                    <text x="700" y="360" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="18" font-weight="700"><!--!-->
+部门主管申核                    </text><!--!-->
+                </a><!--!-->
+                <rect x="625" y="430" rx="10" ry="10" width="150" height="60" style="fill:green;stroke:black;stroke-width:3;opacity:0.5"></rect><!--!-->
+                <a><!--!-->
+                    <text x="700" y="460" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="18" font-weight="700"><!--!-->
+老板申核                    </text><!--!-->
+                </a><!--!-->
+                <rect x="905" y="530" rx="10" ry="10" width="150" height="60" style="fill:green;stroke:black;stroke-width:3;opacity:0.5"></rect><!--!-->
+                <a><!--!-->
+                    <text x="980" y="560" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="18" font-weight="700"><!--!-->
+申核结果通知                    </text><!--!-->
+                </a><!--!-->
+                <rect x="905" y="530" rx="10" ry="10" width="150" height="60" style="fill:green;stroke:black;stroke-width:3;opacity:0.5"></rect><!--!-->
+                <a><!--!-->
+                    <text x="980" y="560" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="18" font-weight="700"><!--!-->
+申核结果通知                    </text><!--!-->
+                </a><!--!-->
+                <circle cx="933.3333333333334" cy="655" r="25 " stroke="black" stroke-width="2" fill="white"></circle><!--!-->
+                <a><!--!-->
+                    <text x="933.3333333333334" y="655" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="16"><!--!-->
+结束                    </text><!--!-->
+                </a><!--!-->
+                <circle cx="933.3333333333334" cy="655" r="25 " stroke="black" stroke-width="2" fill="white"></circle><!--!-->
+                <a><!--!-->
+                    <text x="933.3333333333334" y="655" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="16"><!--!-->
+结束                    </text><!--!-->
+                </a><!--!-->
+<!--!-->
+        <line x1="466.6666666666667" y1="290" x2="700" y2="330" stroke="black" stroke-width="2" marker-end="url(#idArrow)"></line><!--!-->
+        <circle cx="583.3333333333334" cy="310" r="8" stroke="black" stroke-width="1" fill="white"></circle><!--!-->
+        <a><!--!-->
+            <text x="583.3333333333334" y="310" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="12">c</text><!--!-->
+        </a><!--!-->
+        <line x1="700" y1="390" x2="700" y2="430" stroke="black" stroke-width="2" marker-end="url(#idArrow)"></line><!--!-->
+        <circle cx="700" cy="410" r="8" stroke="black" stroke-width="1" fill="white"></circle><!--!-->
+        <a><!--!-->
+            <text x="700" y="410" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="12">c</text><!--!-->
+        </a><!--!-->
+        <line x1="700" y1="390" x2="980" y2="530" stroke="black" stroke-width="2" marker-end="url(#idArrow)"></line><!--!-->
+        <circle cx="840" cy="460" r="8" stroke="black" stroke-width="1" fill="white"></circle><!--!-->
+        <a><!--!-->
+            <text x="840" y="460" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="12">c</text><!--!-->
+        </a><!--!-->
+        <line x1="700" y1="490" x2="980" y2="530" stroke="black" stroke-width="2" marker-end="url(#idArrow)"></line><!--!-->
+        <circle cx="840" cy="510" r="8" stroke="black" stroke-width="1" fill="white"></circle><!--!-->
+        <a><!--!-->
+            <text x="840" y="510" fill="black" alignment-baseline="middle" text-anchor="middle" font-size="12">c</text><!--!-->
+        </a><!--!-->
+<!--!-->
+
+        <line x1="980" y1="590" x2="933.3333333333334" y2="630" stroke="black" stroke-width="2" marker-end="url(#idArrow)"></line><!--!-->
+<!--!-->
+</svg>

+ 158 - 155
wispro.sp.winClient/FlowChart.cs

@@ -9,24 +9,38 @@ namespace wispro.sp.winClient
 {
     public partial class FlowChart
     {
-        double TitleHeight = 60;
-        double ChartWidth = 1000;
-        double rectWidth = 120;
-        double rectHeight = 60;
-        double initRadius = 25;
-        double EndRadius = 25;
-        double hSeparation = 50;
+        [Parameter]
+        public double TitleHeight { get; set; } = 100;
+
 
-        int rectFontSize = 24;
+        [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;
 
-        private List<entity.workflowDefine.Step> Steps { get; set; }
+        int rectFontSize { get; set; } = 18;
 
-        private List<entity.workflowDefine.TrasferCondition> Transfers { get; set; }
+        
+        public List<entity.workflowDefine.Step> Steps { get; set; }
 
-        private List<entity.workflowDefine.Action> Actions { get; set; }
+        
+        public List<entity.workflowDefine.TrasferCondition> Transfers { get; set; }
 
-        [Parameter]
+        
+        public List<entity.workflowDefine.Action> Actions { get; set; }
+
+        
         public entity.workflowDefine.Workflow workflow { get; set; }
 
         internal class shapeNode
@@ -64,9 +78,7 @@ namespace wispro.sp.winClient
 
         private shapeNode FindNode(int stepId, out int Level, List<shapeNode> lstNodes)
         {
-            //Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(lstNodes));
-            Console.WriteLine(stepId.ToString());
-            Console.WriteLine(System.Text.Json.JsonSerializer.Serialize(Steps));
+
             Level = 1;
             foreach (var sNode in lstNodes)
             {
@@ -82,7 +94,7 @@ namespace wispro.sp.winClient
 
                         var retObj = FindNode(stepId, out Level, sNode.Childrens);
 
-                        if(retObj != null)
+                        if (retObj != null)
                         {
                             return retObj;
                         }
@@ -109,7 +121,7 @@ namespace wispro.sp.winClient
                     else
                     {
                         nodes.Add(sNode);
-                        levelNodes.Add(sNode.Level,nodes);
+                        levelNodes.Add(sNode.Level, nodes);
                     }
 
                     GetShapeLevelNodes(levelNodes, sNode.Childrens);
@@ -120,59 +132,12 @@ namespace wispro.sp.winClient
         shapeNode startNode;
         shapeNode InitShape;
         shapeNode endNode;
+        Dictionary<int, List<shapeNode>> LevelNodes = new Dictionary<int, List<shapeNode>>();
+
         private void initShapeTree()
         {
-
-            #region Demo流程数据
-            workflow = new entity.workflowDefine.Workflow();
-            workflow.Name = "请假处理流程";
-            workflow.Id = 1;
-
-            workflow.InitAction = new entity.workflowDefine.Action();
-            workflow.InitAction.Name = "填写请教条";
-
-            Steps = new List<entity.workflowDefine.Step>();
-            var step1 = new entity.workflowDefine.Step();
-            step1.Name = "部门主管申核";
-            step1.Id = 1;
-            Steps.Add(step1);
-
-            var step2 = new entity.workflowDefine.Step();
-            step2.Name = "老板申核";
-            step2.Id = 2;
-            Steps.Add(step2);
-
-            var step3 = new entity.workflowDefine.Step();
-            step3.Name = "申核结果通知";
-            step3.Id = 3;
-            Steps.Add(step3);
-
-            workflow.EndStepId = 3;
-
-            Transfers = new List<entity.workflowDefine.TrasferCondition>();
-            var t = new entity.workflowDefine.TrasferCondition();
-            t.StepId = null;
-            t.nextStepId = 1;
-            Transfers.Add(t);
-
-            var t1 = new entity.workflowDefine.TrasferCondition();
-            t1.StepId = 1;
-            t1.nextStepId = 2;
-            Transfers.Add(t1);
-
-            var t2 = new entity.workflowDefine.TrasferCondition();
-            t2.StepId = 1;
-            t2.nextStepId = 3;
-            Transfers.Add(t2);
-
-            var t3 = new entity.workflowDefine.TrasferCondition();
-            t3.StepId = 2;
-            t3.nextStepId = 3;
-            Transfers.Add(t3);
-            #endregion
-
             shapeTrees = new List<shapeNode>();
-
+            LevelNodes = new Dictionary<int, List<shapeNode>>();
             startNode = new shapeNode()
             {
                 //NodeObject = workflow.InitAction,
@@ -180,16 +145,14 @@ namespace wispro.sp.winClient
                 OutCount = 0,
                 width = 2 * initRadius,
                 height = 2 * initRadius,
-                 Level =0
+                Level = 0
             };
-           
-
             InitShape = new shapeNode()
             {
                 NodeObject = workflow.InitAction,
                 InCount = 0,
                 OutCount = 0,
-                Type = 1                ,
+                Type = 1,
                 Level = 1
             };
 
@@ -209,105 +172,118 @@ namespace wispro.sp.winClient
 
             shapeTrees.Add(startNode);
             
-            //shapeTrees.Add(endNode);
 
             #region 将步骤对象生成形状Node并添加到列表中
-            foreach (var step in Steps)
+            if (Steps == null || Steps.Count == 0 || Steps.FirstOrDefault(s => s.Id == workflow.EndStepId) == null)
             {
-                var temNode = new shapeNode() { NodeObject = step, InCount = 0, OutCount = 0, Type = 1 };
-
-                if (workflow.EndStepId == step.Id)
+                InitShape.Childrens = new List<shapeNode>();
+                InitShape.Childrens.Add(endNode);
+                InitShape.OutCount += 1;
+                endNode.Parents = new List<shapeNode>();
+                endNode.Parents.Add(InitShape);
+                endNode.InCount += 1;
+                endNode.Level = InitShape.Level + 1;
+            }
+            
+                foreach (var step in Steps)
                 {
-                    temNode.Childrens = new List<shapeNode>();
-                    temNode.Childrens.Add(endNode);
-                    endNode.Parents = new List<shapeNode>();
-                    endNode.Parents.Add(temNode);
-                }
+                    var temNode = new shapeNode() { NodeObject = step, InCount = 0, OutCount = 0, Type = 1 };
 
-                shapeTrees.Add(temNode);
-            }
+                    if (workflow.EndStepId == step.Id)
+                    {
+                        temNode.Childrens = new List<shapeNode>();
+                        temNode.Childrens.Add(endNode);
+                        endNode.Parents = new List<shapeNode>();
+                        endNode.Parents.Add(temNode);
+                    }
+
+                    shapeTrees.Add(temNode);
+                }
             #endregion
 
             #region 遍历转移条件,生成流程树
-            foreach (var transfer in Transfers)
+            if (Transfers != null)
             {
-                var FromNode = InitShape;
-                int FromLevel = 0;
-
-                if (transfer.StepId != null)
+                foreach (var transfer in Transfers)
                 {
-                    FromNode = FindNode(transfer.StepId.Value, out FromLevel, shapeTrees);
-                }
+                    var FromNode = InitShape;
+                    int FromLevel = 0;
 
-                int ToLevel = 0;
-                var ToNode = FindNode(transfer.nextStepId, out ToLevel, shapeTrees);
+                    if (transfer.StepId != null)
+                    {
+                        FromNode = FindNode(transfer.StepId.Value, out FromLevel, shapeTrees);
+                    }
 
-                if (FromNode.Childrens == null)
-                {
-                    FromNode.Childrens = new List<shapeNode>();
-                }
-                FromNode.Childrens.Add(ToNode);
+                    int ToLevel = 0;
+                    var ToNode = FindNode(transfer.nextStepId, out ToLevel, shapeTrees);
 
-                if (ToNode.Parents == null)
-                {
-                    ToNode.Parents = new List<shapeNode>();
-                }
-                ToNode.Parents.Add(FromNode);
+                    if (FromNode.Childrens == null)
+                    {
+                        FromNode.Childrens = new List<shapeNode>();
+                    }
+                    FromNode.Childrens.Add(ToNode);
 
-                FromNode.OutCount += 1;
-                ToNode.InCount += 1;
+                    if (ToNode.Parents == null)
+                    {
+                        ToNode.Parents = new List<shapeNode>();
+                    }
+                    ToNode.Parents.Add(FromNode);
 
-                if (FromNode.Level >= ToLevel)
-                {
-                    ToNode.Level = FromNode.Level + 1;
-                }
+                    FromNode.OutCount += 1;
+                    ToNode.InCount += 1;
 
-                if (shapeTrees.Contains(ToNode))
-                {
+                    if (FromNode.Level >= ToLevel)
+                    {
+                        ToNode.Level = FromNode.Level + 1;
+                    }
+
+                    if (shapeTrees.Contains(ToNode))
+                    {
 
-                    shapeTrees.Remove(ToNode);
+                        shapeTrees.Remove(ToNode);
+                    }
                 }
-            }
 
-            endNode.Level = endNode.Parents[0].Level + 1;
+                endNode.Level = endNode.Parents[0].Level + 1;
+            }
             #endregion
 
-            Dictionary<int, List<shapeNode>> LevelNodes = new Dictionary<int, List<shapeNode>>();
+
 
             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<shapeNode>(), Childrens = new List<shapeNode>() };
-                                    parentNode.Childrens.Add(xnNode);
-                                    xnNode.Parents = new List<shapeNode>();
-                                    xnNode.Parents.Add(parentNode);
-                                    parentNode = xnNode;
-                                }
-
-                                parentNode.Childrens.Add(temChildrenNode);
-                                temChildrenNode.Parents.Add(parentNode);
-                            }
-                        }
-                    }
-                }
-            }
+            //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<shapeNode>(), Childrens = new List<shapeNode>() };
+            //                        parentNode.Childrens.Add(xnNode);
+            //                        xnNode.Parents = new List<shapeNode>();
+            //                        xnNode.Parents.Add(parentNode);
+            //                        parentNode = xnNode;
+            //                    }
+
+            //                    parentNode.Childrens.Add(temChildrenNode);
+            //                    temChildrenNode.Parents.Add(parentNode);
+            //                }
+            //            }
+            //        }
+            //    }
+            //}
             #endregion
 
             int MaxNodeLevel = 0;
@@ -350,6 +326,11 @@ namespace wispro.sp.winClient
                 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();
 
@@ -384,15 +365,15 @@ namespace wispro.sp.winClient
 
             ArrangeNodesInRows(LevelNodes);
 
+            
         }
 
-
         private void ArrangeNodesInRows(Dictionary<int, List<shapeNode>> LevelNodes)
         {
 
-            
+
             double preBotton = TitleHeight;
-            for (int iLevel = 0; iLevel < LevelNodes.Count ; iLevel++)
+            for (int iLevel = 0; iLevel < LevelNodes.Count; iLevel++)
             {
                 int iCount = 0;
                 foreach (var node in LevelNodes[iLevel])
@@ -411,8 +392,8 @@ namespace wispro.sp.winClient
                 double maxHeight = 0;
                 foreach (var node in LevelNodes[iLevel])
                 {
-                    
-                    
+
+
                     int max = (node.InCount > node.OutCount) ? node.InCount : node.OutCount;
                     if (max == 0)
                     {
@@ -432,7 +413,7 @@ namespace wispro.sp.winClient
                     }
                     iCount += max;
 
-                    if(node.height > maxHeight)
+                    if (node.height > maxHeight)
                     {
                         maxHeight = node.height;
                     }
@@ -440,10 +421,10 @@ namespace wispro.sp.winClient
 
                 foreach (var node in LevelNodes[iLevel])
                 {
-                    node.y = preBotton + hSeparation + maxHeight/2;
+                    node.y = preBotton + hSeparation + maxHeight / 2;
                 }
 
-                preBotton = preBotton + hSeparation  + maxHeight;
+                preBotton = preBotton + hSeparation + maxHeight;
             }
 
 
@@ -469,13 +450,28 @@ namespace wispro.sp.winClient
             return ret;
         }
 
-        public void OnInitialized()
+        public dynamic GetEndStepLine()
         {
-            initShapeTree();
+            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)
         {
 
@@ -490,6 +486,13 @@ namespace wispro.sp.winClient
         {
 
         }
+
+        void ClickNode(shapeNode node)
+        {
+
+        }
+
+
     }
 
 }

+ 74 - 3
wispro.sp.winClient/Form1.cs

@@ -180,10 +180,81 @@ namespace wispro.sp.winClient
 
         private async void button3_Click(object sender, EventArgs e)
         {
-
+            #region Demo流程数据
+            entity.workflowDefine.Workflow workflow = new entity.workflowDefine.Workflow();
+            workflow.Name = "专案绩效流程";
+            workflow.Id = 1;
+            workflow.EndStepId = -1;
+
+            workflow.InitAction = new entity.workflowDefine.Action();
+            workflow.InitAction.Name = "填写请教条";
+
+            List<entity.workflowDefine.Step> Steps = new List<entity.workflowDefine.Step>();
+            //var step1 = new entity.workflowDefine.Step();
+            //step1.Name = "部门主管申核";
+            //step1.Id = 1;
+            //Steps.Add(step1);
+
+            //var step2 = new entity.workflowDefine.Step();
+            //step2.Name = "老板申核";
+            //step2.Id = 2;
+            //Steps.Add(step2);
+
+            //var step3 = new entity.workflowDefine.Step();
+            //step3.Name = "申核结果通知";
+            //step3.Id = 3;
+            //Steps.Add(step3);
+
+            //var step4 = new entity.workflowDefine.Step();
+            //step4.Name = "新步骤";
+            //step4.Id = 4;
+            //Steps.Add(step4);
+
+            //var step5 = new entity.workflowDefine.Step();
+            //step5.Name = "新步骤";
+            //step5.Id = 5;
+            //Steps.Add(step5);
+
+
+            //workflow.EndStepId = 3;
+
+            List<entity.workflowDefine.TrasferCondition> Transfers = new List<entity.workflowDefine.TrasferCondition>();
+            //var t = new entity.workflowDefine.TrasferCondition();
+            //t.StepId = null;
+            //t.nextStepId = 1;
+            //Transfers.Add(t);
+
+            //var t1 = new entity.workflowDefine.TrasferCondition();
+            //t1.StepId = 1;
+            //t1.nextStepId = 2;
+            //Transfers.Add(t1);
+
+            //var t2 = new entity.workflowDefine.TrasferCondition();
+            //t2.StepId = 1;
+            //t2.nextStepId = 3;
+            //Transfers.Add(t2);
+
+            //var t3 = new entity.workflowDefine.TrasferCondition();
+            //t3.StepId = 2;
+            //t3.nextStepId = 3;
+            //Transfers.Add(t3);
+            #endregion
             FlowChart flowChart = new FlowChart();
-            flowChart.OnInitialized();
-            flowChart.GetLineParater(new entity.workflowDefine.TrasferCondition() { nextStepId = 1 });
+            flowChart.workflow = workflow;
+            flowChart.Steps = Steps;
+            flowChart.Transfers = Transfers;
+
+            var newStep = new entity.workflowDefine.Step();
+            newStep.Name = "新步骤";
+            newStep.workflowId = workflow.Id;
+            Steps.Add(newStep);
+
+            flowChart.Refresh();
+
+            //flowChart.Refresh();
+
+            
+
 
             //var test =wispro.sp.utility.EmunHelper.getEnumDescriptionDic<wispro.sp.entity.workflowDefine.LogicSymbols>();