流程控制,前台可以模拟审批

This commit is contained in:
akwkevin
2021-07-27 16:15:30 +08:00
parent c6d5601558
commit b93160481f
12 changed files with 380 additions and 92 deletions

View File

@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media;
using Util.DiagramDesigner;
@@ -46,13 +47,243 @@ namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
}
}
public static void Approve(FlowNode flowNode, int status, string remark)
public static List<FlowNode> FlowNodes { get; set; }
/// <summary>
/// 初始化数据
/// </summary>
/// <param name="json"></param>
/// <param name="id"></param>
/// <returns></returns>
public static void InitOAData(List<FlowNode> oASteps, List<ConnectorViewModel> connectors)
{
foreach (var edge in connectors)
{
var source = oASteps.FirstOrDefault(p => p.BottomConnector == edge.SourceConnectorInfo || p.LeftConnector == edge.SourceConnectorInfo || p.RightConnector == edge.SourceConnectorInfo);
if (source != null)
{
if (source.Kind == NodeKinds.Decide)
{
source.SelectNextStep.Add((edge.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem.Id.ToString(), "data.Flag" + edge.Text);
}
else if (source.Kind == NodeKinds.COBegin)
{
source.SelectNextStep.Add((edge.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem.Id.ToString(), "True");
}
else
{
source.NextStepId = (edge.SinkConnectorInfo as FullyCreatedConnectorInfo).DataItem.Id.ToString();
}
}
}
foreach (var step in oASteps)
{
//恢复指向上一个节点
if (!string.IsNullOrEmpty(step.NextStepId))
{
var nextstep = oASteps.FirstOrDefault(p => p.Id.ToString() == step.NextStepId);
if (nextstep == null)
throw new Exception(string.Format("流程异常,无法找到{0}的下一个流程节点{1}", step.Id, step.NextStepId));
if (nextstep.Kind == NodeKinds.Decide)
{
var nsteps = oASteps.Where(p => nextstep.SelectNextStep.Any(q => p.Id.ToString() == q.Key));
if (nsteps == null || nsteps.Count() == 0)
throw new Exception(string.Format("流程异常,无法找到{0}的下一个流程节点{1}", step.Id, step.NextStepId));
//跳过Decide指向下面的子节点
foreach (var nstep in nsteps)
{
if (nstep.PreStepId == null)
nstep.PreStepId = new List<string>();
nstep.PreStepId.Add(step.Id.ToString());
}
}
else
{
if (nextstep.PreStepId == null)
nextstep.PreStepId = new List<string>();
nextstep.PreStepId.Add(step.Id.ToString());
}
}
}
var oAStartStep = oASteps.Single(p => p.Kind == NodeKinds.Start);
if (string.IsNullOrEmpty(oAStartStep.NextStepId))
{
throw new Exception("开始节点没有下一个节点");
}
string nextstepid = oAStartStep.NextStepId;
FlowNodes = InitStep(oASteps, nextstepid);
FlowNodes.Insert(0, oAStartStep);
Approve(oAStartStep, 100);
}
/// <summary>
/// 获取下一个节点
/// </summary>
/// <param name="oASteps"></param>
/// <param name="nextstepid"></param>
/// <returns></returns>
public static List<FlowNode> InitStep(List<FlowNode> oASteps, string nextstepid)
{
List<FlowNode> outsteps = new List<FlowNode>();
List<string> nextids = new List<string>();
var step = oASteps.FirstOrDefault(p => p.Id.ToString() == nextstepid);
if (step != null)
{
if (!string.IsNullOrEmpty(step.NextStepId))
{
nextids.Add(step.NextStepId);
}
else if (step.SelectNextStep != null && step.SelectNextStep.Count > 0)
{
nextids.AddRange(step.SelectNextStep.Keys);
}
outsteps.Add(step);
oASteps.Remove(step);
}
int index = outsteps.IndexOf(step);
nextids.Reverse();
foreach (var next in nextids)
{
outsteps.InsertRange(index + 1, InitStep(oASteps, next));
}
return outsteps;
}
public static void Approve(FlowNode flowNode, int status, string remark = null)
{
flowNode.Status = status;
switch (status)
{
case 100:
flowNode.Color = Colors.Green.ToString();
SetStatus(flowNode, status, remark);
flowNode.Color = Colors.Green.ToString();
if (!string.IsNullOrEmpty(flowNode.NextStepId))
{
Next(flowNode.NextStepId);
}
else if (flowNode.SelectNextStep != null && flowNode.SelectNextStep.Count > 0)
{
foreach (var step in flowNode.SelectNextStep)
{
Next(step.Key);
}
}
break;
case 2:
if (Pre(flowNode))
{
SetStatus(flowNode, status, remark);
}
else
{
MessageBox.Show("该节点不支持驳回上一级");
}
break;
case 3:
SetStatus(flowNode, status, remark);
FlowNodes.ForEach(p => { if (p.Status == 100) p.Status = 0; });
Approve(FlowNodes[0], 100);
MessageBox.Show("流程重新开始");
break;
case 4:
SetStatus(flowNode, status, remark);
MessageBox.Show("流程否决");
break;
}
}
public static void Next(string stepid)
{
FlowNode nextNode = FlowNodes.FirstOrDefault(p => p.Id.ToString() == stepid);
SetStatus(nextNode, 1);
switch (nextNode.Kind)
{
case NodeKinds.Start:
SetStatus(nextNode, 100);
Next(nextNode.NextStepId);
break;
case NodeKinds.End:
SetStatus(nextNode, 100);
MessageBox.Show("流程完成");
break;
case NodeKinds.Decide:
foreach (var step in nextNode.SelectNextStep)
{
try
{
//暂未实现表达式比较
step.Value.Replace("data.Flag", nextNode.Text);
//先按第一个表达式成立处理。
SetStatus(nextNode, 100);
Next(step.Key);
break;
}
catch { }
}
break;
case NodeKinds.COBegin:
foreach (var step in nextNode.SelectNextStep)
{
SetStatus(nextNode, 100);
Next(step.Key);
}
break;
case NodeKinds.COEnd:
foreach (var prestep in nextNode.PreStepId)
{
var step = FlowNodes.FirstOrDefault(p => p.Id.ToString() == prestep);
if (step.Status != 100)
{
return;
}
}
SetStatus(nextNode, 100);
Next(nextNode.NextStepId);
break;
}
}
public static bool Pre(FlowNode flowNode)
{
if (flowNode.PreStepId != null && flowNode.PreStepId.Count == 1)
{
FlowNode preNode = FlowNodes.FirstOrDefault(p => p.Id.ToString() == flowNode.PreStepId[0]);
if (preNode.Kind == NodeKinds.Middle)
{
SetStatus(preNode, 1);
return true;
}
}
return false;
}
public static void SetStatus(FlowNode flowNode, int status, string remark = null)
{
flowNode.Status = status;
flowNode.Remark = remark;
switch (status)
{
case 100:
flowNode.Color = Colors.Green.ToString();
break;
case 0:
flowNode.Color = Colors.Yellow.ToString();
break;
case 1:
flowNode.Color = Colors.Orange.ToString();
break;
case 2:
flowNode.Color = Colors.Red.ToString();
@@ -65,7 +296,5 @@ namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
break;
}
}
}
}

View File

@@ -36,13 +36,13 @@ namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
{
base.Init();
DesignerItemViewModelBase start = new StartFlowNode() { Left = 100, Top = 0, Color = Colors.Green.ToString() };
DesignerItemViewModelBase start = new StartFlowNode() { Left = 100, Top = 0, Color = Colors.Yellow.ToString() };
DiagramViewModel.DirectAddItemCommand.Execute(start);
DesignerItemViewModelBase middle1 = new MiddleFlowNode() { Left = 100, Top = 100, Color = Colors.Yellow.ToString(), Text = "主管审批" };
DiagramViewModel.DirectAddItemCommand.Execute(middle1);
DesignerItemViewModelBase decide = new DecideFlowNode() { Left = 100, Top = 200, Color = Colors.Yellow.ToString(), Text = "条件" };
DesignerItemViewModelBase decide = new DecideFlowNode() { Left = 100, Top = 200, Color = Colors.Yellow.ToString(), Text = "5" };
DiagramViewModel.DirectAddItemCommand.Execute(decide);
DesignerItemViewModelBase middle2 = new MiddleFlowNode() { Left = 200, Top = 300, Color = Colors.Yellow.ToString(), Text = "分管领导" };
@@ -96,6 +96,8 @@ namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
DiagramViewModel.DirectAddItemCommand.Execute(connector10);
DiagramViewModel.ClearSelectedItems();
FlowchartService.InitOAData(DiagramViewModel.Items.OfType<FlowNode>().ToList(), DiagramViewModel.Items.OfType<ConnectorViewModel>().ToList());
}

View File

@@ -1,4 +1,5 @@
using AIStudio.Wpf.ADiagram.Helpers;
using AIStudio.Wpf.ADiagram.Services;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -12,11 +13,12 @@ namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
{
public class FlowNode : DesignerItemViewModelBase
{
protected IUIVisualizerService visualiserService;
public FlowNode(NodeKinds kind) : base()
{
Kind = kind;
Text = Kind.GetDescription();
ShowText = true;
Text = Kind.GetDescription();
ItemWidth = 80;
ItemHeight = 40;
@@ -32,6 +34,10 @@ namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
base.Init();
ShowRotate = false;
ShowText = true;
IsReadOnlyText = true;
visualiserService = ApplicationServicesProvider.Instance.Provider.VisualizerService;
}
protected override void LoadDesignerItemViewModel(IDiagramViewModel parent, SelectableDesignerItemBase designerbase)
@@ -68,7 +74,7 @@ namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
[Browsable(false)]
public string StateImage { get; set; }
#region 使
#region 使,
private int _status;
public int Status
@@ -89,6 +95,10 @@ namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
SetProperty(ref _remark, value);
}
}
public List<string> PreStepId { get; set; }
public string NextStepId { get; set; }
public Dictionary<string, string> SelectNextStep { get; set; } = new Dictionary<string, string>();
#endregion
}

View File

@@ -42,8 +42,14 @@
<Border BorderThickness="1" BorderBrush="{Binding ColorViewModel.LineColor,Converter={StaticResource ColorBrushConverter}}" Background="{Binding ColorViewModel.FillColor,Converter={StaticResource ColorBrushConverter}}">
<Grid>
<Border HorizontalAlignment="Left" Width="3" Background="{Binding Color}"/>
</Grid>
</Border>
<TextBlock Text="{Binding Remark}" FontSize="9" RenderTransformOrigin="1,0.5" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="2">
<TextBlock.RenderTransform>
<TranslateTransform X="{Binding ItemWidth}"/>
</TextBlock.RenderTransform>
</TextBlock>
</Grid>
</ControlTemplate>
@@ -109,7 +115,7 @@
<Viewbox Stretch="Fill">
<Grid>
<ContentControl Style="{StaticResource CustomFlowNodeStyle}" Margin="2"/>
<TextBlock Text="{Binding Text}" Margin="5" Foreground="{Binding ColorViewModel.LineColor,Converter={StaticResource ColorBrushConverter}}" VerticalAlignment="Center" HorizontalAlignment="Center" />
<TextBlock Text="{Binding Text}" Margin="5" Foreground="{Binding ColorViewModel.LineColor,Converter={StaticResource ColorBrushConverter}}" HorizontalAlignment="Left" />
</Grid>
</Viewbox>
</Grid>
@@ -126,4 +132,18 @@
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type demo:MiddleFlowNodeData}">
<Grid Background="{DynamicResource Fluent.Ribbon.Brushes.AccentBaseColorBrush}">
<StackPanel Orientation="Vertical">
<StackPanel Margin="5" Orientation="Horizontal" >
<RadioButton Margin="3" IsChecked="{Binding Status,Converter={converter:ConverterValueMapToBool Parameter=100}, ConverterParameter=100}" Content="通过"/>
<RadioButton Margin="3" IsChecked="{Binding Status,Converter={converter:ConverterValueMapToBool Parameter=2}, ConverterParameter=2}" Content="驳回上一级"/>
<RadioButton Margin="3" IsChecked="{Binding Status,Converter={converter:ConverterValueMapToBool Parameter=3}, ConverterParameter=3}" Content="驳回重提"/>
<RadioButton Margin="3" IsChecked="{Binding Status,Converter={converter:ConverterValueMapToBool Parameter=4}, ConverterParameter=4}" Content="否决"/>
</StackPanel>
<TextBox Height="28" Margin="5" Text="{Binding Remark}" VerticalContentAlignment="Center"></TextBox>
</StackPanel>
</Grid>
</DataTemplate>
</ResourceDictionary>

View File

@@ -1,10 +1,7 @@
using AIStudio.Wpf.ADiagram.Controls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using Util.DiagramDesigner;
namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
@@ -56,5 +53,25 @@ namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
SetProperty(ref _actType, value);
}
}
protected override void ExecuteEditCommand(object param)
{
if (IsReadOnly == true) return;
if (Status == 1)
{
MiddleFlowNodeData data = new MiddleFlowNodeData();
if (visualiserService.ShowDialog(data) == true)
{
FlowchartService.Approve(this, data.Status, data.Remark);
}
}
else
{
MessageBox.Show("该节点不能进行审批!!!");
}
}
}
}

View File

@@ -0,0 +1,38 @@
using AIStudio.Wpf.ADiagram.Models;
namespace AIStudio.Wpf.ADiagram.Demos.Flowchart
{
public class MiddleFlowNodeData : TitleBindableBase
{
public MiddleFlowNodeData()
{
Title = "审批";
}
private int _status = 100;
public int Status
{
get
{
return _status;
}
set
{
SetProperty(ref _status, value);
}
}
private string _remark;
public string Remark
{
get
{
return _remark;
}
set
{
SetProperty(ref _remark, value);
}
}
}
}

View File

@@ -1,19 +0,0 @@
<Window x:Class="AIStudio.Wpf.ADiagram.Demos.Flowchart.Views.ApproveWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:AIStudio.Wpf.ADiagram.Demos.Flowchart.Views"
xmlns:converter="clr-namespace:AIStudio.Wpf.ADiagram.Converters"
mc:Ignorable="d"
Title="ApproveWindow" Height="450" Width="800">
<Grid>
<StackPanel Margin="50,5,0,5" Orientation="Horizontal" >
<RadioButton Margin="3" IsChecked="{Binding Status,Converter={converter:ConverterValueMapToBool Parameter=100}, ConverterParameter=100}" Content="通过"/>
<RadioButton Margin="3" IsChecked="{Binding Status,Converter={converter:ConverterValueMapToBool Parameter=2}, ConverterParameter=2}" Content="驳回上一级"/>
<RadioButton Margin="3" IsChecked="{Binding Status,Converter={converter:ConverterValueMapToBool Parameter=3}, ConverterParameter=3}" Content="驳回重提"/>
<RadioButton Margin="3" IsChecked="{Binding Status,Converter={converter:ConverterValueMapToBool Parameter=4}, ConverterParameter=4}" Content="否决"/>
</StackPanel>
<TextBox Height="28" Margin="52,5,0,5" Text="{Binding Remark}" ></TextBox>
</Grid>
</Window>

View File

@@ -1,55 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Util.DiagramDesigner;
namespace AIStudio.Wpf.ADiagram.Demos.Flowchart.Views
{
/// <summary>
/// ApproveWindow.xaml 的交互逻辑
/// </summary>
public partial class ApproveWindow : Window
{
public ApproveWindow()
{
InitializeComponent();
}
}
public class ApproveWindowViewModel : BindableBase
{
private int _status;
public int Status
{
get
{
return _status;
}
set
{
SetProperty(ref _status, value);
}
}
private string _remark;
public string Remark
{
get
{
return _remark;
}
set
{
SetProperty(ref _remark, value);
}
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace Util.DiagramDesigner
{
public class InvertBoolConverter : MarkupExtension, IValueConverter
{
/// <summary>
/// Returns the value for the target property of this markup extension.
/// </summary>
/// <param name="serviceProvider">Object that can provide services for the markup extension.</param>
/// <returns>Reference to the instance of this Int32IndexToNumberConverter.</returns>
public override object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return !(bool)value;
}
}
}

View File

@@ -22,7 +22,8 @@
<s:ArrowSizeConverter x:Key="ArrowSizeConverter"/>
<s:LineDashConverter x:Key="LineDashConverter"/>
<s:ClipConverter x:Key="ClipConverter"/>
<s:InvertBoolConverter x:Key="InvertBoolConverter"/>
<!-- ResizeDecorator Default Template -->
<!--
<ControlTemplate x:Key="ResizeDecoratorTemplate" TargetType="{x:Type Control}">
@@ -376,7 +377,7 @@
VerticalAlignment="Stretch"
Content="{TemplateBinding Content}" />
<Control x:Name="PART_Text" Margin="5">
<Control x:Name="PART_Text" Margin="5" IsHitTestVisible="{Binding IsReadOnlyText,Converter={StaticResource InvertBoolConverter}}">
<Control.Style>
<Style TargetType="Control">
<Setter Property="Template">

View File

@@ -82,7 +82,7 @@
<TextBox x:Name="PART_ShowText"
Text="{Binding Text, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Foreground="{Binding FontViewModel.FontColor,Converter={StaticResource ColorBrushConverter}}"
IsReadOnly="{Binding IsReadOnly}"
IsReadOnly="{Binding IsReadOnlyText}"
FontSize="{Binding FontViewModel.FontSize}"
FontFamily="{Binding FontViewModel.FontFamily}"
FontWeight="{Binding FontViewModel.FontWeight}"

View File

@@ -234,6 +234,21 @@ namespace Util.DiagramDesigner
}
}
private bool _isReadOnlyText = false;
public bool IsReadOnlyText
{
get
{
if (IsReadOnly)
return true;
return _isReadOnlyText;
}
set
{
SetProperty(ref _isReadOnlyText, value);
}
}
private bool _showText;
public virtual bool ShowText
{