using Prism.Mvvm; using Prism.Commands; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Windows; using TeamAAS.FlowEditor.Models; using TeamAAS.FlowEngine.Execution; using TeamAAS.Dialogs; using TeamAAS.FlowEngine; using TeamAAS.FlowEditor.Execution; namespace TeamAAS.FlowEditor { /// /// 流程编辑器外壳 ViewModel:管理多 Tab 流程 /// public class FlowEditorShellViewModel : TeamAAS.BindableBase { public ObservableCollection FlowTabs => PluginLoader.Instance.FlowTabs; private FlowTabItem _activeTab; public FlowTabItem ActiveTab { get => _activeTab; set { if (_activeTab != null) _activeTab.IsActive = false; if (SetProperty(ref _activeTab, value)) { if (_activeTab != null) _activeTab.IsActive = true; RaisePropertyChanged(nameof(ActiveEditorVm)); } } } public FlowEditorViewModel ActiveEditorVm => ActiveTab?.EditorVm; // 流程结构变化(新建/删除/重命名 Tab)——关闭的 Tab 不再参与 per-tab 脏检查,需单独记录 private bool _structureChanged; private bool _isReadOnly; /// 只读监控模式(运行产品运行中):由 ProductManager/ProductViewModel 设置并传播到所有 Tab public bool IsReadOnly { get => _isReadOnly; set { if (SetProperty(ref _isReadOnly, value)) ApplyReadOnlyToTabs(); } } private bool _isMonitorMode; /// 是否监控运行中流程(勾选后挂接 RunTabs,否则独立编辑) public bool IsMonitorMode { get => _isMonitorMode; private set => SetProperty(ref _isMonitorMode, value); } /// 切换监控模式(开/关) public DelegateCommand ToggleMonitorCommand { get; } private bool _isMonitorOptionAvailable; /// 「监控当前流程」选项是否可用(当前产品==运行产品且正在运行) public bool IsMonitorOptionAvailable { get => _isMonitorOptionAvailable; private set => SetProperty(ref _isMonitorOptionAvailable, value); } /// 把当前只读状态应用到所有流程 Tab(切换产品/新增 Tab 后调用) public void ApplyReadOnlyToTabs() { foreach (var tab in FlowTabs) tab.EditorVm?.SetReadOnly(_isReadOnly); } /// 是否存在未保存的修改(流程内容修改 + 流程增删/改名) public bool HasUnsavedChanges => _structureChanged || FlowTabs.Any(t => t.EditorVm != null && t.EditorVm.IsDirty); /// 保存后清除所有流程的修改标记 public void MarkAllClean() { _structureChanged = false; foreach (var tab in FlowTabs) tab.EditorVm?.MarkClean(); } public DelegateCommand AddFlowCommand { get; } public DelegateCommand CloseFlowCommand { get; } public DelegateCommand RenameFlowCommand { get; } public DelegateCommand OpenTabPropertiesCommand { get; } public DelegateCommand ImportFlowCommand { get; } public DelegateCommand ExportFlowCommand { get; } public FlowEditorShellViewModel() { if (FlowTabs.Count == 0) { var defaultTab = new FlowTabItem("流程1"); FlowTabs.Add(defaultTab); ActiveTab = defaultTab; } else { ActiveTab = FlowTabs[0]; } AddFlowCommand = new DelegateCommand(AddFlow); CloseFlowCommand = new DelegateCommand(CloseFlow); RenameFlowCommand = new DelegateCommand(tab => RenameFlow(tab)); OpenTabPropertiesCommand = new DelegateCommand(OpenTabProperties); // 导入/导出逻辑统一在 FlowEditorViewModel 的命令里,Shell 只做转发(只读模式下禁用导入) ImportFlowCommand = new DelegateCommand( () => ActiveEditorVm?.ImportFlowCommand?.Execute(), () => ActiveEditorVm != null && !IsReadOnly); ExportFlowCommand = new DelegateCommand(() => ActiveEditorVm?.ExportFlowCommand?.Execute()); ToggleMonitorCommand = new DelegateCommand(ToggleMonitor); // 订阅运行态重载事件:监控中主界面重载流程时自动同步显示 FlowRunner.Instance.RunTabsRefreshed += OnRunTabsRefreshed; } /// 切换监控模式入口 private void ToggleMonitor() { if (IsMonitorMode) ExitMonitor(); else EnterMonitor(); } /// 进入监控模式:必要时提示保存当前编辑,再挂接运行态 RunTabs private void EnterMonitor() { // 运行态未启动 → 提示并 return if (!FlowRunner.Instance.IsRunning || FlowRunner.Instance.RunTabs.Count == 0) { DialogHelper.Info("当前没有正在运行的流程,无法监控"); return; } // 当前产品 != 运行产品 → 提示保存后切换(点否不保存但仍然切到运行产品监控) if (!string.Equals(ProductManager.Instance.CurrentProductName, FlowRunner.Instance.RunProductName, StringComparison.Ordinal)) { if (HasUnsavedChanges) { bool save = DialogHelper.ConfirmYesNo("切换到运行产品需先保存当前方案,是否保存?", "保存确认"); if (save) SaveAllModifiedTabs(); } ProductManager.Instance.SwitchProduct(FlowRunner.Instance.RunProductName, this); } else if (HasUnsavedChanges) { // 当前产品 == 运行产品:点否也进入监控,但不保存 bool save = DialogHelper.ConfirmYesNo("进入监控前是否保存当前修改?", "保存确认"); if (save) SaveAllModifiedTabs(); } AttachRunTabs(); IsMonitorMode = true; IsReadOnly = true; } /// 退出监控模式:恢复编辑态(从磁盘重载副本) private void ExitMonitor() { IsMonitorMode = false; IsReadOnly = false; DetachAndReloadFromDisk(); } /// /// 根据"当前产品 == 运行产品 且 正在运行"刷新监控选项可用性,并自动同步监控状态: /// - 满足条件 → 选项可用;若当前不在监控则自动进入监控 /// - 不满足 → 选项禁用;若当前在监控则自动退出 /// 该方法由 ProductManager.SwitchProduct/SwitchProductAsync、ProductViewModel.RefreshRunState 调用。 /// 注意:本方法不直接调 EnterMonitor(避免与 EnterMonitor 内部的 SwitchProduct 互相递归), /// 而是设置 IsMonitorMode 标志后再调,让 EnterMonitor 看到 IsMonitorMode=true 时跳过自动调用。 /// public void ApplyMonitorAvailability() { bool available = !string.IsNullOrEmpty(ProductManager.Instance.CurrentProductName) && string.Equals(ProductManager.Instance.CurrentProductName, FlowRunner.Instance.RunProductName, StringComparison.Ordinal) && FlowRunner.Instance.IsRunning; IsMonitorOptionAvailable = available; try { if (!available && IsMonitorMode) { // 当前产品不再是运行产品或运行已停止 → 退出监控 ExitMonitor(); } else if (available && !IsMonitorMode) { // 切到运行产品且正在运行 → 默认进入监控模式。 // 先置 IsMonitorMode=true 防止 EnterMonitor 内部调 SwitchProduct 时 // 其末尾的 ApplyMonitorAvailability 再次触发 EnterMonitor(无限递归)。 IsMonitorMode = true; EnterMonitor(); } } catch (Exception ex) { // 监控自动切换失败不应导致整个应用闪退 IsMonitorMode = false; IsReadOnly = false; TeamAAS.AppLogger.Error("ApplyMonitorAvailability 自动监控失败", ex, nameof(FlowEditorShellViewModel)); } } /// 保存所有已修改的流程 tab 到磁盘 private void SaveAllModifiedTabs() { foreach (var tab in FlowTabs) if (tab.EditorVm != null && tab.EditorVm.IsDirty) ProductManager.Instance.SaveFlow(ProductManager.Instance.CurrentProductName, tab); MarkAllClean(); } /// /// 释放当前集合中的编辑态 Tab(插件 Dispose + Debug 注册表清理)。 /// 跳过 FlowRunner.RunTabs 中的运行态 Tab——它们由 FlowRunner 拥有,可能正在执行,不能在此释放。 /// private void DisposeEditTabs() { try { var runTabs = FlowRunner.Instance.RunTabs; var editTabs = FlowTabs.Where(t => t != null && !runTabs.Contains(t)).ToList(); if (editTabs.Count > 0) ProductManager.DisposeFlowTabs(editTabs); } catch (Exception ex) { TeamAAS.AppLogger.Error("释放编辑态流程 Tab 失败", ex, nameof(FlowEditorShellViewModel)); } } /// 挂接运行态 RunTabs 到当前编辑器(监控模式核心) private void AttachRunTabs() { // 释放被替换的编辑态 Tab,否则插件与 Debug 注册表引用会随每次进入监控持续泄漏 DisposeEditTabs(); // 进入监控=换对象:完全清空 Debug 之前的编辑态集合(含子流程残留), // 此后编辑器挂的是 Main 的 RunTabs,看到的就是运行态数据 ResultRegistry.Debug.ClearAllFlows(); FlowTabs.Clear(); foreach (var t in FlowRunner.Instance.RunTabs.ToList()) FlowTabs.Add(t); if (FlowTabs.Count > 0) ActiveTab = FlowTabs[0]; } /// 从磁盘重载当前产品的编辑副本(退出监控时恢复编辑态) private void DetachAndReloadFromDisk() { var productName = ProductManager.Instance.CurrentProductName; // 退出监控时 FlowTabs 挂的是 RunTabs(由 FlowRunner 拥有),只解除引用不释放; // 非监控状态下的残留编辑 Tab 则正常释放 DisposeEditTabs(); // 退出监控=换回编辑态对象:完全清空 Debug(含监控期与子流程残留),再从磁盘重载干净副本 ResultRegistry.Debug.ClearAllFlows(); FlowTabs.Clear(); if (!string.IsNullOrEmpty(productName)) { foreach (var t in ProductManager.Instance.LoadAllFlows(productName)) FlowTabs.Add(t); } if (FlowTabs.Count == 0) { var defaultTab = new FlowTabItem("流程1"); FlowTabs.Add(defaultTab); } ActiveTab = FlowTabs.FirstOrDefault(); } /// FlowRunner.RunTabsRefreshed 回调:监控模式下自动同步新运行态 private void OnRunTabsRefreshed() { if (!IsMonitorMode) return; Application.Current?.Dispatcher.Invoke(AttachRunTabs); } public void AddFlow() { if (IsReadOnly) return; // 运行监控中禁止新增流程 var name = GetUniqueFlowName("流程"); var tab = new FlowTabItem(name); tab.EditorVm?.SetReadOnly(IsReadOnly); FlowTabs.Add(tab); ActiveTab = tab; _structureChanged = true; // 新增流程属于未保存修改 } private string GetUniqueFlowName(string baseName) { int suffix = 1; var name = baseName + suffix; while (FlowTabs.Any(t => t.Name == name)) { suffix++; name = baseName + suffix; } return name; } public void CloseFlow(FlowTabItem tab) { if (tab == null || FlowTabs.Count <= 1) return; if (IsReadOnly) { DialogHelper.Info("运行产品监控中,无法关闭流程"); return; } // 关闭的流程如果有未保存修改,先确认是否放弃 if (tab.EditorVm != null && tab.EditorVm.IsDirty) { bool discard = DialogHelper.ConfirmYesNo( $"流程 \"{tab.Name}\" 有未保存的修改,关闭后将丢失这些修改,确定关闭吗?", "关闭流程"); if (!discard) return; } int idx = FlowTabs.IndexOf(tab); FlowTabs.Remove(tab); if (ActiveTab == tab) { int newIdx = Math.Min(idx, FlowTabs.Count - 1); ActiveTab = FlowTabs[newIdx]; } _structureChanged = true; // 删除流程:保存时需要同步删除磁盘上的对应 .aas } public void RenameFlow(FlowTabItem tab, string newName = null) { if (tab == null) return; if (IsReadOnly) { DialogHelper.Info("运行产品监控中,无法重命名流程"); return; } if (newName == null) { newName = ShowRenameDialog(tab.Name); if (string.IsNullOrWhiteSpace(newName)) return; } // 重名加序号(排除自身) if (FlowTabs.Any(t => t != tab && t.Name == newName)) newName = GetUniqueFlowName(newName); tab.Name = newName; tab.Graph.GraphName = newName; // 同步所有节点的 FlowName foreach (var node in tab.Graph.Nodes) { node.FlowName = newName; } tab.EditorVm?.MarkDirty(); // 流程改名也属于未保存修改 } public void OpenTabProperties(FlowTabItem tab) { if (tab == null) return; if (IsReadOnly) { DialogHelper.Info("运行产品监控中,无法修改流程属性"); return; } // 调试:记录打开属性前的 TriggerType 值 TeamAAS.AppLogger.Info($"OpenTabProperties: {tab.Name} 当前 TriggerType={tab.TriggerType}", "OpenTabProperties"); if (DialogHelper.EditProperties(tab, $"流程属性 - {tab.Name}")) { // 调试:记录关闭对话框后的 TriggerType 值 TeamAAS.AppLogger.Info($"OpenTabProperties: {tab.Name} 修改后 TriggerType={tab.TriggerType}", "OpenTabProperties"); DialogHelper.Info($"已更新流程属性: {tab.Name}"); } } private string ShowRenameDialog(string currentName) { return DialogHelper.Input("流程名称", "重命名流程", currentName); } } }