错误程序子程序优化
This commit is contained in:
@@ -46,11 +46,18 @@ namespace ACP.ViewModels
|
|||||||
|
|
||||||
#region 子程序导航
|
#region 子程序导航
|
||||||
|
|
||||||
/// <summary>
|
public bool CanGoBack =>
|
||||||
/// 当前台架是否可以返回上一级
|
(CurrentContext?.MainNav.CanGoBack ?? false) ||
|
||||||
/// </summary>
|
(CurrentContext?.ErrorNav.CanGoBack ?? false);
|
||||||
public bool CanGoBack => CurrentContext?.CanGoBack ?? false;
|
|
||||||
|
|
||||||
|
private void OnGoBack()
|
||||||
|
{
|
||||||
|
var ctx = CurrentContext;
|
||||||
|
if (ctx == null) return;
|
||||||
|
// 工具栏按钮同时重置两个 Tab 的导航
|
||||||
|
ctx.MainNav.Reset(ctx.Program, "主程序");
|
||||||
|
ctx.ErrorNav.Reset(ctx.Program, "错误程序");
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -167,6 +174,7 @@ namespace ACP.ViewModels
|
|||||||
public ICommand GetFileStringCommand { get; set; }
|
public ICommand GetFileStringCommand { get; set; }
|
||||||
public ICommand SilenceBuzzerCommand { get; set; }
|
public ICommand SilenceBuzzerCommand { get; set; }
|
||||||
public ICommand SettingChannelCommand { get; set; }
|
public ICommand SettingChannelCommand { get; set; }
|
||||||
|
public ICommand GoBackCommand { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public ShellViewModel(IContainerProvider containerProvider)
|
public ShellViewModel(IContainerProvider containerProvider)
|
||||||
@@ -218,6 +226,7 @@ namespace ACP.ViewModels
|
|||||||
SilenceBuzzerCommand = new AsyncDelegateCommand(SilenceBuzzer);
|
SilenceBuzzerCommand = new AsyncDelegateCommand(SilenceBuzzer);
|
||||||
SettingChannelCommand = new AsyncDelegateCommand(SilenceBuzzer);
|
SettingChannelCommand = new AsyncDelegateCommand(SilenceBuzzer);
|
||||||
SettingChannelCommand = new DelegateCommand(SettingChannel);
|
SettingChannelCommand = new DelegateCommand(SettingChannel);
|
||||||
|
GoBackCommand = new DelegateCommand(OnGoBack);
|
||||||
_globalInfo.ContextDic.Add("default", new ScopedContext());
|
_globalInfo.ContextDic.Add("default", new ScopedContext());
|
||||||
|
|
||||||
_eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(() =>
|
_eventAggregator.GetEvent<LoginSuccessEvent>().Subscribe(() =>
|
||||||
|
|||||||
@@ -245,7 +245,15 @@ Command="{Binding GetFileStringCommand}">
|
|||||||
</MenuItem.Icon>
|
</MenuItem.Icon>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Separator/>
|
<Separator/>
|
||||||
|
<!-- 返回上一级程序 -->
|
||||||
|
<MenuItem Header="返回上一级程序"
|
||||||
|
Foreground="Black"
|
||||||
|
Command="{Binding GoBackCommand}">
|
||||||
|
<MenuItem.Icon>
|
||||||
|
<materialDesign:PackIcon Kind="ArrowLeftBold"
|
||||||
|
Foreground="Black" />
|
||||||
|
</MenuItem.Icon>
|
||||||
|
</MenuItem>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem Header="切换初始主界面"
|
<MenuItem Header="切换初始主界面"
|
||||||
FontSize="13"
|
FontSize="13"
|
||||||
|
|||||||
@@ -568,6 +568,8 @@ namespace TestingModule.ViewModels
|
|||||||
{
|
{
|
||||||
// 查找最近的未匹配循环开始
|
// 查找最近的未匹配循环开始
|
||||||
StepVM? lastUnmatchedLoopStart = null;
|
StepVM? lastUnmatchedLoopStart = null;
|
||||||
|
if (_ScopedContext.SelectedStepList == "主程序")
|
||||||
|
{
|
||||||
for (int i = Program.StepCollection.Count - 1; i >= 0; i--)
|
for (int i = Program.StepCollection.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (Program.StepCollection[i].StepType == "循环开始")
|
if (Program.StepCollection[i].StepType == "循环开始")
|
||||||
@@ -581,7 +583,23 @@ namespace TestingModule.ViewModels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = Program.ErrorStepCollection.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (Program.ErrorStepCollection[i].StepType == "循环开始")
|
||||||
|
{
|
||||||
|
bool isMatched = Program.ErrorStepCollection.Any(s => s.StepType == "循环结束" && s.LoopStartStepId == Program.StepCollection[i].ID);
|
||||||
|
|
||||||
|
if (!isMatched)
|
||||||
|
{
|
||||||
|
lastUnmatchedLoopStart = Program.ErrorStepCollection[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
var newStep = new StepVM
|
var newStep = new StepVM
|
||||||
{
|
{
|
||||||
Name = "循环结束",
|
Name = "循环结束",
|
||||||
|
|||||||
@@ -99,39 +99,24 @@ namespace TestingModule.ViewModels
|
|||||||
public ICommand GoBackCommand { get; set; }
|
public ICommand GoBackCommand { get; set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 子程序导航
|
#region 子程序导航(主程序 / 错误程序各自独立)
|
||||||
|
|
||||||
|
// --- 主程序 Tab 导航属性 ---
|
||||||
|
public ProgramVM MainCurrentProgram => _ScopedContext.MainNav.CurrentProgram;
|
||||||
|
public string MainBreadcrumbPath => _ScopedContext.MainNav.BreadcrumbPath;
|
||||||
|
public bool MainCanGoBack => _ScopedContext.MainNav.CanGoBack;
|
||||||
|
public ObservableCollection<StepVM> MainDisplaySteps => _ScopedContext.MainNav.DisplaySteps;
|
||||||
|
|
||||||
|
// --- 错误程序 Tab 导航属性 ---
|
||||||
|
public ProgramVM ErrorCurrentProgram => _ScopedContext.ErrorNav.CurrentProgram;
|
||||||
|
public string ErrorBreadcrumbPath => _ScopedContext.ErrorNav.BreadcrumbPath;
|
||||||
|
public bool ErrorCanGoBack => _ScopedContext.ErrorNav.CanGoBack;
|
||||||
|
public ObservableCollection<StepVM> ErrorDisplaySteps => _ScopedContext.ErrorNav.DisplaySteps;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前显示的程序(绑定到 DataGrid 的 ItemsSource 根)。
|
/// 当前激活的导航状态(根据选中的 Tab 决定)
|
||||||
/// 委托到 ScopedContext.CurrentProgram,保证 ShellViewModel 也能通过 ScopedContext 访问。
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ProgramVM CurrentProgram
|
private ProgramNavigationState ActiveNav => _ScopedContext.ActiveNav;
|
||||||
{
|
|
||||||
get => _ScopedContext.CurrentProgram;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_ScopedContext.CurrentProgram != value)
|
|
||||||
{
|
|
||||||
_ScopedContext.CurrentProgram = value;
|
|
||||||
RaisePropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string BreadcrumbPath
|
|
||||||
{
|
|
||||||
get => _ScopedContext.BreadcrumbPath;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_ScopedContext.BreadcrumbPath != value)
|
|
||||||
{
|
|
||||||
_ScopedContext.BreadcrumbPath = value;
|
|
||||||
RaisePropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanGoBack => _ScopedContext.CanGoBack;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -153,19 +138,36 @@ namespace TestingModule.ViewModels
|
|||||||
Program.PropertyChanged += Program_PropertyChanged;
|
Program.PropertyChanged += Program_PropertyChanged;
|
||||||
Admin = _globalInfo.IsAdmin;
|
Admin = _globalInfo.IsAdmin;
|
||||||
|
|
||||||
// 初始化 CurrentProgram 为根程序
|
// 初始化两套导航状态
|
||||||
_ScopedContext.CurrentProgram = Program;
|
_ScopedContext.MainNav.Initialize(Program, "主程序");
|
||||||
_ScopedContext.BreadcrumbPath = "主程序";
|
_ScopedContext.ErrorNav.Initialize(Program, "错误程序");
|
||||||
|
|
||||||
// 订阅 ScopedContext 属性变化,外部(ShellViewModel)修改时转发通知给 UI
|
// 订阅主程序导航状态变化 → 转发给 UI
|
||||||
_ScopedContext.PropertyChanged += (s, e) =>
|
_ScopedContext.MainNav.PropertyChanged += (s, e) =>
|
||||||
{
|
{
|
||||||
if (e.PropertyName == nameof(ScopedContext.CurrentProgram))
|
if (e.PropertyName == nameof(ProgramNavigationState.CurrentProgram))
|
||||||
RaisePropertyChanged(nameof(CurrentProgram));
|
{
|
||||||
else if (e.PropertyName == nameof(ScopedContext.BreadcrumbPath))
|
RaisePropertyChanged(nameof(MainCurrentProgram));
|
||||||
RaisePropertyChanged(nameof(BreadcrumbPath));
|
RaisePropertyChanged(nameof(MainDisplaySteps));
|
||||||
else if (e.PropertyName == nameof(ScopedContext.CanGoBack))
|
}
|
||||||
RaisePropertyChanged(nameof(CanGoBack));
|
else if (e.PropertyName == nameof(ProgramNavigationState.BreadcrumbPath))
|
||||||
|
RaisePropertyChanged(nameof(MainBreadcrumbPath));
|
||||||
|
else if (e.PropertyName == nameof(ProgramNavigationState.CanGoBack))
|
||||||
|
RaisePropertyChanged(nameof(MainCanGoBack));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 订阅错误程序导航状态变化 → 转发给 UI
|
||||||
|
_ScopedContext.ErrorNav.PropertyChanged += (s, e) =>
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(ProgramNavigationState.CurrentProgram))
|
||||||
|
{
|
||||||
|
RaisePropertyChanged(nameof(ErrorCurrentProgram));
|
||||||
|
RaisePropertyChanged(nameof(ErrorDisplaySteps));
|
||||||
|
}
|
||||||
|
else if (e.PropertyName == nameof(ProgramNavigationState.BreadcrumbPath))
|
||||||
|
RaisePropertyChanged(nameof(ErrorBreadcrumbPath));
|
||||||
|
else if (e.PropertyName == nameof(ProgramNavigationState.CanGoBack))
|
||||||
|
RaisePropertyChanged(nameof(ErrorCanGoBack));
|
||||||
};
|
};
|
||||||
|
|
||||||
// 订阅子程序导航事件(运行时由 StepRunning 发布)
|
// 订阅子程序导航事件(运行时由 StepRunning 发布)
|
||||||
@@ -272,45 +274,38 @@ namespace TestingModule.ViewModels
|
|||||||
#region 子程序导航方法
|
#region 子程序导航方法
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 编辑模式:右键打开子程序
|
/// 编辑模式:右键打开子程序(操作当前激活的 Tab 的导航状态)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OpenSubProgram()
|
private void OpenSubProgram()
|
||||||
{
|
{
|
||||||
if (SelectedStep == null || SelectedStep.StepType != "子程序" || SelectedStep.SubProgram == null)
|
if (SelectedStep == null || SelectedStep.StepType != "子程序" || SelectedStep.SubProgram == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 压栈:保存当前程序
|
var nav = ActiveNav;
|
||||||
_ScopedContext.SubProgramNavigationStack.Push(CurrentProgram);
|
nav.NavigationStack.Push(nav.CurrentProgram);
|
||||||
_ScopedContext.UpdateCanGoBack();
|
nav.UpdateCanGoBack();
|
||||||
// 更新面包屑
|
|
||||||
var stepName = SelectedStep.Name ?? "子程序";
|
var stepName = SelectedStep.Name ?? "子程序";
|
||||||
BreadcrumbPath = $"{BreadcrumbPath} > {stepName}";
|
nav.BreadcrumbPath = $"{nav.BreadcrumbPath} > {stepName}";
|
||||||
// 切换到子程序
|
nav.CurrentProgram = SelectedStep.SubProgram;
|
||||||
CurrentProgram = SelectedStep.SubProgram;
|
|
||||||
RaisePropertyChanged(nameof(CanGoBack));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 返回上一级程序
|
/// 返回上一级程序(操作当前激活的 Tab 的导航状态)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void GoBack()
|
private void GoBack()
|
||||||
{
|
{
|
||||||
if (_ScopedContext.SubProgramNavigationStack.Count == 0)
|
var nav = ActiveNav;
|
||||||
|
if (nav.NavigationStack.Count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 出栈:恢复上一级程序
|
var parentProgram = nav.NavigationStack.Pop();
|
||||||
var parentProgram = _ScopedContext.SubProgramNavigationStack.Pop();
|
nav.UpdateCanGoBack();
|
||||||
_ScopedContext.UpdateCanGoBack();
|
nav.CurrentProgram = parentProgram;
|
||||||
CurrentProgram = parentProgram;
|
|
||||||
|
|
||||||
// 更新面包屑:去掉最后一段
|
var parts = nav.BreadcrumbPath.Split(" > ");
|
||||||
var parts = BreadcrumbPath.Split(" > ");
|
nav.BreadcrumbPath = parts.Length > 1
|
||||||
if (parts.Length > 1)
|
? string.Join(" > ", parts.Take(parts.Length - 1))
|
||||||
BreadcrumbPath = string.Join(" > ", parts.Take(parts.Length - 1));
|
: nav == _ScopedContext.MainNav ? "主程序" : "错误程序";
|
||||||
else
|
|
||||||
BreadcrumbPath = "主程序";
|
|
||||||
|
|
||||||
RaisePropertyChanged(nameof(CanGoBack));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -318,18 +313,25 @@ namespace TestingModule.ViewModels
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnSubProgramNavigate(SubProgramNavigatePayload payload)
|
private void OnSubProgramNavigate(SubProgramNavigatePayload payload)
|
||||||
{
|
{
|
||||||
|
var nav = payload.IsErrorProgram ? _ScopedContext.ErrorNav : _ScopedContext.MainNav;
|
||||||
if (payload.Action == NavigateAction.Enter && payload.SubProgram != null)
|
if (payload.Action == NavigateAction.Enter && payload.SubProgram != null)
|
||||||
{
|
{
|
||||||
_ScopedContext.SubProgramNavigationStack.Push(CurrentProgram);
|
nav.NavigationStack.Push(nav.CurrentProgram);
|
||||||
_ScopedContext.UpdateCanGoBack();
|
nav.UpdateCanGoBack();
|
||||||
CurrentProgram = payload.SubProgram;
|
nav.CurrentProgram = payload.SubProgram;
|
||||||
var stepName = payload.StepName ?? "子程序";
|
var stepName = payload.StepName ?? "子程序";
|
||||||
BreadcrumbPath = $"{BreadcrumbPath} > {stepName}";
|
nav.BreadcrumbPath = $"{nav.BreadcrumbPath} > {stepName}";
|
||||||
RaisePropertyChanged(nameof(CanGoBack));
|
|
||||||
}
|
}
|
||||||
else if (payload.Action == NavigateAction.Exit)
|
else if (payload.Action == NavigateAction.Exit)
|
||||||
{
|
{
|
||||||
GoBack();
|
if (nav.NavigationStack.Count == 0) return;
|
||||||
|
var parentProgram = nav.NavigationStack.Pop();
|
||||||
|
nav.UpdateCanGoBack();
|
||||||
|
nav.CurrentProgram = parentProgram;
|
||||||
|
var parts = nav.BreadcrumbPath.Split(" > ");
|
||||||
|
nav.BreadcrumbPath = parts.Length > 1
|
||||||
|
? string.Join(" > ", parts.Take(parts.Length - 1))
|
||||||
|
: nav == _ScopedContext.MainNav ? "主程序" : "错误程序";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,23 +17,23 @@
|
|||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Grid>
|
<Grid>
|
||||||
<GroupBox Header="{Binding Title}">
|
<GroupBox Header="{Binding Title}">
|
||||||
<DockPanel>
|
|
||||||
<!-- 面包屑导航栏 -->
|
|
||||||
<Border DockPanel.Dock="Top" Background="#F0F0F0" Padding="6,3" Visibility="{Binding CanGoBack, Converter={StaticResource BooleanToVisibilityConverter}}">
|
|
||||||
<DockPanel>
|
|
||||||
<Button DockPanel.Dock="Right" Content="↩ 返回上一级"
|
|
||||||
Command="{Binding GoBackCommand}"
|
|
||||||
Style="{StaticResource MaterialDesignFlatButton}"
|
|
||||||
FontSize="12" Padding="8,2" Margin="8,0,0,0"/>
|
|
||||||
<TextBlock Text="{Binding BreadcrumbPath}" VerticalAlignment="Center" FontSize="12" Foreground="#555"/>
|
|
||||||
</DockPanel>
|
|
||||||
</Border>
|
|
||||||
<TabControl SelectedIndex="{Binding SelectedTabIndex, Mode=TwoWay}">
|
<TabControl SelectedIndex="{Binding SelectedTabIndex, Mode=TwoWay}">
|
||||||
<i:Interaction.Behaviors>
|
<i:Interaction.Behaviors>
|
||||||
<behaviors:TabControlSelectionChangedBehavior Command="{Binding TabSelectionChangedCommand}"
|
<behaviors:TabControlSelectionChangedBehavior Command="{Binding TabSelectionChangedCommand}"
|
||||||
CommandParameter="{Binding SelectedTabHeader}" />
|
CommandParameter="{Binding SelectedTabHeader}" />
|
||||||
</i:Interaction.Behaviors>
|
</i:Interaction.Behaviors>
|
||||||
<TabItem Header="主程序">
|
<TabItem Header="主程序">
|
||||||
|
<DockPanel>
|
||||||
|
<!-- 主程序面包屑导航栏 -->
|
||||||
|
<Border DockPanel.Dock="Top" Background="#F0F0F0" Padding="6,3" Visibility="{Binding MainCanGoBack, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||||
|
<DockPanel>
|
||||||
|
<Button DockPanel.Dock="Right" Content="↩ 返回上一级"
|
||||||
|
Command="{Binding GoBackCommand}"
|
||||||
|
Style="{StaticResource MaterialDesignFlatButton}"
|
||||||
|
FontSize="12" Padding="8,2" Margin="8,0,0,0"/>
|
||||||
|
<TextBlock Text="{Binding MainBreadcrumbPath}" VerticalAlignment="Center" FontSize="12" Foreground="#555"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
<DataGrid dd:DragDrop.IsDragSource="{Binding Admin}"
|
<DataGrid dd:DragDrop.IsDragSource="{Binding Admin}"
|
||||||
dd:DragDrop.IsDropTarget="{Binding Admin}"
|
dd:DragDrop.IsDropTarget="{Binding Admin}"
|
||||||
dd:DragDrop.UseDefaultDragAdorner="{Binding Admin}"
|
dd:DragDrop.UseDefaultDragAdorner="{Binding Admin}"
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
CanUserAddRows="False"
|
CanUserAddRows="False"
|
||||||
CanUserSortColumns="False"
|
CanUserSortColumns="False"
|
||||||
ItemsSource="{Binding CurrentProgram.StepCollection}"
|
ItemsSource="{Binding MainDisplaySteps}"
|
||||||
SelectedItem="{Binding SelectedStep, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
|
SelectedItem="{Binding SelectedStep, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
|
||||||
SelectionMode="Extended"
|
SelectionMode="Extended"
|
||||||
SelectionUnit="FullRow">
|
SelectionUnit="FullRow">
|
||||||
@@ -144,8 +144,20 @@
|
|||||||
|
|
||||||
|
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
</DockPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
<TabItem Header="错误程序">
|
<TabItem Header="错误程序">
|
||||||
|
<DockPanel>
|
||||||
|
<!-- 错误程序面包屑导航栏 -->
|
||||||
|
<Border DockPanel.Dock="Top" Background="#F0F0F0" Padding="6,3" Visibility="{Binding ErrorCanGoBack, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||||
|
<DockPanel>
|
||||||
|
<Button DockPanel.Dock="Right" Content="↩ 返回上一级"
|
||||||
|
Command="{Binding GoBackCommand}"
|
||||||
|
Style="{StaticResource MaterialDesignFlatButton}"
|
||||||
|
FontSize="12" Padding="8,2" Margin="8,0,0,0"/>
|
||||||
|
<TextBlock Text="{Binding ErrorBreadcrumbPath}" VerticalAlignment="Center" FontSize="12" Foreground="#555"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
<DataGrid dd:DragDrop.IsDragSource="True"
|
<DataGrid dd:DragDrop.IsDragSource="True"
|
||||||
dd:DragDrop.IsDropTarget="True"
|
dd:DragDrop.IsDropTarget="True"
|
||||||
dd:DragDrop.UseDefaultDragAdorner="True"
|
dd:DragDrop.UseDefaultDragAdorner="True"
|
||||||
@@ -153,7 +165,7 @@
|
|||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
CanUserAddRows="False"
|
CanUserAddRows="False"
|
||||||
CanUserSortColumns="False"
|
CanUserSortColumns="False"
|
||||||
ItemsSource="{Binding CurrentProgram.ErrorStepCollection}"
|
ItemsSource="{Binding ErrorDisplaySteps}"
|
||||||
SelectedItem="{Binding SelectedStep, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
|
SelectedItem="{Binding SelectedStep, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
|
||||||
SelectionMode="Extended"
|
SelectionMode="Extended"
|
||||||
SelectionUnit="FullRow">
|
SelectionUnit="FullRow">
|
||||||
@@ -251,9 +263,9 @@
|
|||||||
</DataGrid.ContextMenu>
|
</DataGrid.ContextMenu>
|
||||||
|
|
||||||
</DataGrid>
|
</DataGrid>
|
||||||
|
</DockPanel>
|
||||||
</TabItem>
|
</TabItem>
|
||||||
</TabControl>
|
</TabControl>
|
||||||
</DockPanel>
|
|
||||||
</GroupBox>
|
</GroupBox>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -0,0 +1,89 @@
|
|||||||
|
using Prism.Mvvm;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using UIShare.UIViewModel;
|
||||||
|
|
||||||
|
namespace UIShare.GlobalVariable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 单个 Tab(主程序 / 错误程序)的子程序导航状态。
|
||||||
|
/// 主程序和错误程序各自持有一个独立实例,互不干扰。
|
||||||
|
/// </summary>
|
||||||
|
public class ProgramNavigationState : BindableBase
|
||||||
|
{
|
||||||
|
/// <summary>是否为错误程序 Tab</summary>
|
||||||
|
public bool IsErrorTab { get; set; }
|
||||||
|
|
||||||
|
public Stack<ProgramVM> NavigationStack { get; } = new();
|
||||||
|
|
||||||
|
private ProgramVM _currentProgram;
|
||||||
|
public ProgramVM CurrentProgram
|
||||||
|
{
|
||||||
|
get => _currentProgram;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _currentProgram, value))
|
||||||
|
RaisePropertyChanged(nameof(DisplaySteps));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DataGrid 实际绑定的步骤集合。
|
||||||
|
/// 根程序 + 错误 Tab → ErrorStepCollection;其他情况 → StepCollection。
|
||||||
|
/// </summary>
|
||||||
|
public ObservableCollection<StepVM> DisplaySteps
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (CurrentProgram == null)
|
||||||
|
return new ObservableCollection<StepVM>();
|
||||||
|
// 在根程序且是错误 Tab → 显示错误步骤集合
|
||||||
|
if (NavigationStack.Count == 0 && IsErrorTab)
|
||||||
|
return CurrentProgram.ErrorStepCollection;
|
||||||
|
// 其他情况(主 Tab 或已进入子程序)→ 显示正常步骤集合
|
||||||
|
return CurrentProgram.StepCollection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _breadcrumbPath = "主程序";
|
||||||
|
public string BreadcrumbPath
|
||||||
|
{
|
||||||
|
get => _breadcrumbPath;
|
||||||
|
set => SetProperty(ref _breadcrumbPath, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _canGoBack;
|
||||||
|
public bool CanGoBack
|
||||||
|
{
|
||||||
|
get => _canGoBack;
|
||||||
|
set => SetProperty(ref _canGoBack, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 用根程序初始化导航状态
|
||||||
|
/// </summary>
|
||||||
|
public void Initialize(ProgramVM rootProgram, string label)
|
||||||
|
{
|
||||||
|
NavigationStack.Clear();
|
||||||
|
CurrentProgram = rootProgram;
|
||||||
|
BreadcrumbPath = label;
|
||||||
|
CanGoBack = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空导航栈,回到根程序
|
||||||
|
/// </summary>
|
||||||
|
public void Reset(ProgramVM rootProgram, string label)
|
||||||
|
{
|
||||||
|
NavigationStack.Clear();
|
||||||
|
CurrentProgram = rootProgram;
|
||||||
|
BreadcrumbPath = label;
|
||||||
|
CanGoBack = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据栈状态刷新 CanGoBack
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateCanGoBack() => CanGoBack = NavigationStack.Count > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,66 +19,29 @@ namespace UIShare.GlobalVariable
|
|||||||
{
|
{
|
||||||
private static readonly Random _randomSeed = new Random();
|
private static readonly Random _randomSeed = new Random();
|
||||||
|
|
||||||
#region 子程序导航栈(台架隔离)
|
#region 子程序导航(主程序 / 错误程序各自独立)
|
||||||
|
|
||||||
|
/// <summary>主程序 Tab 的导航状态</summary>
|
||||||
|
public ProgramNavigationState MainNav { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>错误程序 Tab 的导航状态</summary>
|
||||||
|
public ProgramNavigationState ErrorNav { get; } = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 子程序导航栈:记录从根程序进入子程序的路径。
|
/// 根据当前选中的 Tab 返回对应的导航状态
|
||||||
/// 每个台架(ScopedContext 实例)各自维护一个独立的栈,互不干扰。
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Stack<ProgramVM> SubProgramNavigationStack { get; } = new();
|
public ProgramNavigationState ActiveNav =>
|
||||||
|
SelectedStepList == "错误程序" ? ErrorNav : MainNav;
|
||||||
private ProgramVM _currentProgram;
|
|
||||||
/// <summary>
|
|
||||||
/// 当前在 StepManager 中显示的程序(可能是根程序,也可能是某层子程序)。
|
|
||||||
/// UI 的 DataGrid 绑定此属性,而非直接绑定 Program。
|
|
||||||
/// </summary>
|
|
||||||
public ProgramVM CurrentProgram
|
|
||||||
{
|
|
||||||
get => _currentProgram;
|
|
||||||
set => SetProperty(ref _currentProgram, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string _breadcrumbPath = "主程序";
|
|
||||||
/// <summary>
|
|
||||||
/// 面包屑导航路径,如 "主程序 > 连接流程 > 初始化"
|
|
||||||
/// </summary>
|
|
||||||
public string BreadcrumbPath
|
|
||||||
{
|
|
||||||
get => _breadcrumbPath;
|
|
||||||
set => SetProperty(ref _breadcrumbPath, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _canGoBack;
|
|
||||||
/// <summary>
|
|
||||||
/// 是否可以返回上一级(导航栈不为空时为 true)
|
|
||||||
/// </summary>
|
|
||||||
public bool CanGoBack
|
|
||||||
{
|
|
||||||
get => _canGoBack;
|
|
||||||
set => SetProperty(ref _canGoBack, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 清空导航栈,回到根程序界面
|
/// 重置两个 Tab 的导航栈,回到根程序
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void ResetNavigation()
|
public void ResetNavigation()
|
||||||
{
|
{
|
||||||
SubProgramNavigationStack.Clear();
|
MainNav.Reset(Program, "主程序");
|
||||||
CurrentProgram = Program;
|
ErrorNav.Reset(Program, "错误程序");
|
||||||
BreadcrumbPath = "主程序";
|
|
||||||
CanGoBack = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 根据栈状态刷新 CanGoBack
|
|
||||||
/// </summary>
|
|
||||||
public void UpdateCanGoBack() => CanGoBack = SubProgramNavigationStack.Count > 0;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前导航深度(0 = 根程序,1 = 第一层子程序,以此类推)
|
|
||||||
/// </summary>
|
|
||||||
public int NavigationDepth => SubProgramNavigationStack.Count;
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public ProgramVM Program { get; set; } = new();
|
public ProgramVM Program { get; set; } = new();
|
||||||
@@ -109,6 +72,7 @@ namespace UIShare.GlobalVariable
|
|||||||
public int DebugRandomId { get; private set; }
|
public int DebugRandomId { get; private set; }
|
||||||
public ScopedContext()
|
public ScopedContext()
|
||||||
{
|
{
|
||||||
|
ErrorNav.IsErrorTab = true;
|
||||||
lock (_randomSeed)
|
lock (_randomSeed)
|
||||||
{
|
{
|
||||||
// 每次诞生一个新上下文,就在 10000 到 99999 之间随机摇一个数
|
// 每次诞生一个新上下文,就在 10000 到 99999 之间随机摇一个数
|
||||||
|
|||||||
@@ -69,11 +69,11 @@ namespace UIShare
|
|||||||
tmpParameters.Clear();
|
tmpParameters.Clear();
|
||||||
TestRoundID = Guid.NewGuid();
|
TestRoundID = Guid.NewGuid();
|
||||||
}
|
}
|
||||||
|
int initialLoopStackCount = loopStack.Count;
|
||||||
foreach (var item in program.Parameters)
|
foreach (var item in program.Parameters)
|
||||||
{
|
{
|
||||||
tmpParameters.TryAdd(item.ID, item);
|
tmpParameters.TryAdd(item.ID, item);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (index < program.ErrorStepCollection.Count)
|
while (index < program.ErrorStepCollection.Count)
|
||||||
{
|
{
|
||||||
if (_disposed || cancellationToken.IsCancellationRequested)
|
if (_disposed || cancellationToken.IsCancellationRequested)
|
||||||
@@ -188,14 +188,16 @@ namespace UIShare
|
|||||||
Scope = _systemConfig.Title,
|
Scope = _systemConfig.Title,
|
||||||
Action = NavigateAction.Enter,
|
Action = NavigateAction.Enter,
|
||||||
SubProgram = step.SubProgram,
|
SubProgram = step.SubProgram,
|
||||||
StepName = step.Name
|
StepName = step.Name,
|
||||||
|
IsErrorProgram = true
|
||||||
});
|
});
|
||||||
stepSuccess = await ExecuteSteps(step.SubProgram, depth + 1, cancellationToken);
|
stepSuccess = await ExecuteSteps(step.SubProgram, depth + 1, cancellationToken);
|
||||||
// 发布退出子程序导航事件
|
// 发布退出子程序导航事件
|
||||||
_eventAggregator.GetEvent<SubProgramNavigateEvent>().Publish(new SubProgramNavigatePayload
|
_eventAggregator.GetEvent<SubProgramNavigateEvent>().Publish(new SubProgramNavigatePayload
|
||||||
{
|
{
|
||||||
Scope = _systemConfig.Title,
|
Scope = _systemConfig.Title,
|
||||||
Action = NavigateAction.Exit
|
Action = NavigateAction.Exit,
|
||||||
|
IsErrorProgram = true
|
||||||
});
|
});
|
||||||
UpdateCurrentStepResult(step, true, stepSuccess, depth);
|
UpdateCurrentStepResult(step, true, stepSuccess, depth);
|
||||||
if (SubSingleStep)
|
if (SubSingleStep)
|
||||||
@@ -238,7 +240,12 @@ namespace UIShare
|
|||||||
await SaveStepRecordAsync(step, depth, true);
|
await SaveStepRecordAsync(step, depth, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bool finalResult = loopStack.Count == initialLoopStackCount && stepSuccess;
|
||||||
|
|
||||||
|
if (depth > 0) // 子程序
|
||||||
|
{
|
||||||
|
return finalResult;
|
||||||
|
}
|
||||||
return loopStack.Count == 0 && stepSuccess;
|
return loopStack.Count == 0 && stepSuccess;
|
||||||
}
|
}
|
||||||
public async Task<bool> ExecuteSteps(ProgramVM program, int depth = 0, CancellationToken cancellationToken = default)
|
public async Task<bool> ExecuteSteps(ProgramVM program, int depth = 0, CancellationToken cancellationToken = default)
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ namespace UIShare.PubEvent
|
|||||||
|
|
||||||
/// <summary>子程序步骤的名称(用于面包屑显示)</summary>
|
/// <summary>子程序步骤的名称(用于面包屑显示)</summary>
|
||||||
public string? StepName { get; set; }
|
public string? StepName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>是否为错误程序 Tab 的导航(默认 false = 主程序 Tab)</summary>
|
||||||
|
public bool IsErrorProgram { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum NavigateAction
|
public enum NavigateAction
|
||||||
|
|||||||
Reference in New Issue
Block a user