diff --git a/ADP/ViewModels/ShellViewModel.cs b/ADP/ViewModels/ShellViewModel.cs index b656b16..f39b168 100644 --- a/ADP/ViewModels/ShellViewModel.cs +++ b/ADP/ViewModels/ShellViewModel.cs @@ -154,6 +154,24 @@ namespace ADP.ViewModels public ICommand MonitorValueSettingCommand { get; set; } public ICommand GetFileStringCommand { get; set; } public ICommand SilenceBuzzerCommand { get; set; } + public ICommand GoBackCommand { get; set; } + #endregion + + #region 子程序导航 + + public bool CanGoBack => + (CurrentContext?.MainNav.CanGoBack ?? false) || + (CurrentContext?.ErrorNav.CanGoBack ?? false); + + private void OnGoBack() + { + var ctx = CurrentContext; + if (ctx == null) return; + // 工具栏按钮同时重置两个 Tab 的导航 + ctx.MainNav.Reset(ctx.Program, "主程序"); + ctx.ErrorNav.Reset(ctx.Program, "错误程序"); + } + #endregion public ShellViewModel(IContainerProvider containerProvider) @@ -188,6 +206,7 @@ namespace ADP.ViewModels MonitorValueSettingCommand = new DelegateCommand(MonitorValueSetting); GetFileStringCommand = new DelegateCommand(GetFileString); SilenceBuzzerCommand = new AsyncDelegateCommand(SilenceBuzzer); + GoBackCommand = new DelegateCommand(OnGoBack); _globalInfo.ContextDic.Add("default", new ScopedContext()); @@ -457,6 +476,9 @@ namespace ADP.ViewModels LoggerHelper.InfoWithNotify(runningScope, $"{_globalInfo.UserName} 执行工位 [{runningScope}] 复位命令"); + // 重置导航栈,回到根程序界面 + targetContext.ResetNavigation(); + _executionTasks.TryGetValue(runningScope, out var currentTask); _errorExecutionTasks.TryGetValue(runningScope, out var errorTask); @@ -573,6 +595,7 @@ namespace ADP.ViewModels targetContext.Program.Parameters.Clear(); targetContext.Program.StepCollection.Clear(); targetContext.Program.ErrorStepCollection.Clear(); + targetContext.ResetNavigation(); foreach (var item in CurrentConfig.ParameterList) { var param = CurrentConfig.SharedParameterList.FirstOrDefault(x => x.ParameterName == item.Name); diff --git a/ADP/Views/ShellView.xaml b/ADP/Views/ShellView.xaml index 88df17c..a1b0306 100644 --- a/ADP/Views/ShellView.xaml +++ b/ADP/Views/ShellView.xaml @@ -226,6 +226,16 @@ Command="{Binding GetFileStringCommand}"> Foreground="Black" /> + + + + + + + = 0; i--) + if (_ScopedContext.SelectedStepList == "主程序") { - if (Program.StepCollection[i].StepType == "循环开始") + for (int i = Program.StepCollection.Count - 1; i >= 0; i--) { - bool isMatched = Program.StepCollection.Any(s => s.StepType == "循环结束" && s.LoopStartStepId == Program.StepCollection[i].ID); - - if (!isMatched) + if (Program.StepCollection[i].StepType == "循环开始") { - lastUnmatchedLoopStart = Program.StepCollection[i]; - break; + bool isMatched = Program.StepCollection.Any(s => s.StepType == "循环结束" && s.LoopStartStepId == Program.StepCollection[i].ID); + + if (!isMatched) + { + lastUnmatchedLoopStart = Program.StepCollection[i]; + break; + } + } + } + } + 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; + } } } } diff --git a/TestingModule/ViewModels/StepsManagerViewModel.cs b/TestingModule/ViewModels/StepsManagerViewModel.cs index 23e7ad4..8df45a3 100644 --- a/TestingModule/ViewModels/StepsManagerViewModel.cs +++ b/TestingModule/ViewModels/StepsManagerViewModel.cs @@ -94,6 +94,29 @@ namespace TestingModule.ViewModels public ICommand DeleteStepCommand { get;set; } public ICommand TabSelectionChangedCommand { get;set; } public ICommand SelectionChangedCommand { get;set; } + public ICommand OpenSubProgramCommand { get; set; } + public ICommand GoBackCommand { get; set; } + #endregion + + #region 子程序导航(主程序 / 错误程序各自独立) + + // --- 主程序 Tab 导航属性 --- + public ProgramVM MainCurrentProgram => _ScopedContext.MainNav.CurrentProgram; + public string MainBreadcrumbPath => _ScopedContext.MainNav.BreadcrumbPath; + public bool MainCanGoBack => _ScopedContext.MainNav.CanGoBack; + public ObservableCollection 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 ErrorDisplaySteps => _ScopedContext.ErrorNav.DisplaySteps; + + /// + /// 当前激活的导航状态(根据选中的 Tab 索引决定) + /// + private ProgramNavigationState ActiveNav => SelectedTabIndex == 1 ? _ScopedContext.ErrorNav : _ScopedContext.MainNav; + #endregion public StepsManagerViewModel(IContainerProvider containerProvider, ScopedContext scopedContext, SystemConfig systemConfig, GlobalInfo globalInfo) : base(containerProvider) @@ -108,9 +131,48 @@ namespace TestingModule.ViewModels DeleteStepCommand = new DelegateCommand(DeleteStep); TabSelectionChangedCommand = new DelegateCommand(TabSelectionChanged); SelectionChangedCommand = new DelegateCommand(SelectionChanged); + OpenSubProgramCommand = new DelegateCommand(OpenSubProgram); + GoBackCommand = new DelegateCommand(GoBack); SubscribeStepCollections(); Program.PropertyChanged += Program_PropertyChanged; Admin = _globalInfo.IsAdmin; + + // 初始化两套导航状态 + _ScopedContext.MainNav.Initialize(Program, "主程序"); + _ScopedContext.ErrorNav.Initialize(Program, "错误程序"); + + // 订阅主程序导航状态变化 → 转发给 UI + _ScopedContext.MainNav.PropertyChanged += (s, e) => + { + if (e.PropertyName == nameof(ProgramNavigationState.CurrentProgram)) + { + RaisePropertyChanged(nameof(MainCurrentProgram)); + RaisePropertyChanged(nameof(MainDisplaySteps)); + } + 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 发布) + _eventAggregator.GetEvent() + .Subscribe(OnSubProgramNavigate, ThreadOption.UIThread, false, + payload => payload.Scope == _systemConfig.Title); } private void SelectionChanged(object parameter) @@ -208,6 +270,72 @@ namespace TestingModule.ViewModels } #endregion + #region 子程序导航方法 + + /// + /// 编辑模式:右键打开子程序(操作当前激活的 Tab 的导航状态) + /// + private void OpenSubProgram() + { + if (SelectedStep == null || SelectedStep.StepType != "子程序" || SelectedStep.SubProgram == null) + return; + + var nav = ActiveNav; + nav.NavigationStack.Push(nav.CurrentProgram); + nav.UpdateCanGoBack(); + var stepName = SelectedStep.Name ?? "子程序"; + nav.BreadcrumbPath = $"{nav.BreadcrumbPath} > {stepName}"; + nav.CurrentProgram = SelectedStep.SubProgram; + } + + /// + /// 返回上一级程序(操作当前激活的 Tab 的导航状态) + /// + private void GoBack() + { + var nav = ActiveNav; + 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 ? "主程序" : "错误程序"; + } + + /// + /// 运行时:处理 StepRunning 发布的子程序导航事件 + /// + private void OnSubProgramNavigate(SubProgramNavigatePayload payload) + { + var nav = payload.IsErrorProgram ? _ScopedContext.ErrorNav : _ScopedContext.MainNav; + if (payload.Action == NavigateAction.Enter && payload.SubProgram != null) + { + nav.NavigationStack.Push(nav.CurrentProgram); + nav.UpdateCanGoBack(); + nav.CurrentProgram = payload.SubProgram; + var stepName = payload.StepName ?? "子程序"; + nav.BreadcrumbPath = $"{nav.BreadcrumbPath} > {stepName}"; + } + else if (payload.Action == NavigateAction.Exit) + { + 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 ? "主程序" : "错误程序"; + } + } + + #endregion + #region 辅助方法 /// diff --git a/TestingModule/Views/StepsManager.xaml b/TestingModule/Views/StepsManager.xaml index e7b3fca..2d044ca 100644 --- a/TestingModule/Views/StepsManager.xaml +++ b/TestingModule/Views/StepsManager.xaml @@ -8,9 +8,13 @@ xmlns:behaviors="clr-namespace:UIShare.Behaviors;assembly=UIShare" xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes" xmlns:prism="http://prismlibrary.com/" + xmlns:converters="clr-namespace:UIShare.Converters;assembly=UIShare" d:DesignHeight="450" d:DesignWidth="800" mc:Ignorable="d"> + + + @@ -19,6 +23,17 @@ CommandParameter="{Binding SelectedTabHeader}" /> + + + + +