using System.Collections.ObjectModel; using System.ComponentModel; using System.Windows; using System.Windows.Input; namespace SHH.CameraDashboard { /// /// 客户端配置向导的 ViewModel /// 实现了 接口,用于与父窗口的蒙板交互。 /// public class WizardClientsViewModel : INotifyPropertyChanged, IOverlayClosable { /// /// 绑定到 ListView 的数据源 /// public static ObservableCollection ServiceNodes => AppGlobal.ServiceNodes; #region --- 业务数据属性 --- private string _statusText = "准备就绪"; /// /// 获取或设置向导的当前状态文本。 /// public string StatusText { get => _statusText; set { _statusText = value; OnPropertyChanged(); } } #endregion #region --- 命令属性 --- /// /// 获取用于确认操作的命令。 /// public ICommand ConfirmCommand { get; } /// /// 获取用于取消操作的命令。 /// public ICommand CancelCommand { get; } // 新增一行的命令 public ICommand AddNodeCommand { get; } public ICommand DeleteNodeCommand { get; } public ICommand CheckCommand { get; } #endregion #region --- 构造函数 --- /// /// 初始化 类的新实例。 /// public WizardClientsViewModel() { // 实现新增逻辑 AddNodeCommand = new RelayCommand(_ => { // 创建新行对象并添加到集合 var newNode = new ServiceNodeModel { ServiceNodeName = "新节点", ServiceNodeIp = "0.0.0.0", ServiceNodePort = "5000", Status = "未检测" }; ServiceNodes.Add(newNode); }); // 删除逻辑实现 DeleteNodeCommand = new RelayCommand(node => { if (node != null && ServiceNodes.Contains(node)) { ServiceNodes.Remove(node); } }); // 初始化确认命令 ConfirmCommand = new RelayCommand(async _ => { try { // 2. 【核心代码】调用通用存储服务 // 泛型 T 自动推断为 ObservableCollection await LocalStorageService.SaveAsync( AppPaths.ServiceNodesConfig, // 路径:Configs/service_nodes.json ServiceNodes // 数据:当前的列表对象 ); // 4. 关闭当前弹窗 RequestClose?.Invoke(); } catch (Exception ex) { MessageBox.Show($"保存失败:{ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } }); CheckCommand = new RelayCommand(async _ => await ExecuteCheckAsync()); // 初始化取消命令 CancelCommand = new RelayCommand(_ => { // 直接触发关闭请求事件,取消并关闭向导 RequestClose?.Invoke(); }); } #endregion #region --- IOverlayClosable 接口实现 --- /// /// 当需要关闭蒙板时发生。 /// public event Action? RequestClose; #endregion #region --- INotifyPropertyChanged 接口实现 --- /// /// 当属性值更改时发生。 /// public event PropertyChangedEventHandler? PropertyChanged; /// /// 引发 事件。 /// /// 更改的属性名称。 protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } #endregion private async Task ExecuteCheckAsync() { foreach (var node in ServiceNodes) { if (string.IsNullOrWhiteSpace(node.ServiceNodeIp) || string.IsNullOrWhiteSpace(node.ServiceNodePort)) continue; node.Status = "正在获取信息..."; // 1. 调用 ApiClient 获取列表 (代码不需要变,因为 Repository 已经封装好了) var cameras = await ApiClient.Instance.Cameras.GetListByAddressAsync( node.ServiceNodeIp, node.ServiceNodePort, "向导页面" ); // 2. 根据返回的详细数据,生成更智能的状态描述 if (cameras.Count > 0) { // 统计在线的摄像头数量 int onlineCount = 0; // 统计主要品牌 (例如: HikVision) string firstBrand = cameras[0].Brand; foreach (var cam in cameras) { if (cam.IsOnline) onlineCount++; } // 状态显示示例:只能是在线,其它的影响界面上色 node.Status = $"在线"; } else { // 列表为空,或者是网络不通导致 Repository 返回了空列表 // 为了区分是“无数据”还是“网络不通”,其实 Repository 可以优化返回 null 或抛异常, // 但目前的架构返回空列表最安全。 node.Status = "离线或无设备"; } } } } }