Files
Ayay/SHH.CameraDashboard/ViewModels/WizardClientsVewModel.cs

188 lines
6.2 KiB
C#
Raw Permalink Normal View History

2026-01-01 22:40:32 +08:00
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Input;
namespace SHH.CameraDashboard
{
/// <summary>
/// 客户端配置向导的 ViewModel
/// 实现了 <see cref="IOverlayClosable"/> 接口,用于与父窗口的蒙板交互。
/// </summary>
public class WizardClientsViewModel : INotifyPropertyChanged, IOverlayClosable
{
/// <summary>
/// 绑定到 ListView 的数据源
/// </summary>
public static ObservableCollection<ServiceNodeModel> ServiceNodes
=> AppGlobal.ServiceNodes;
#region --- ---
private string _statusText = "准备就绪";
/// <summary>
/// 获取或设置向导的当前状态文本。
/// </summary>
public string StatusText
{
get => _statusText;
set { _statusText = value; OnPropertyChanged(); }
}
#endregion
#region --- ---
/// <summary>
/// 获取用于确认操作的命令。
/// </summary>
public ICommand ConfirmCommand { get; }
/// <summary>
/// 获取用于取消操作的命令。
/// </summary>
public ICommand CancelCommand { get; }
// 新增一行的命令
public ICommand AddNodeCommand { get; }
public ICommand DeleteNodeCommand { get; }
public ICommand CheckCommand { get; }
#endregion
#region --- ---
/// <summary>
/// 初始化 <see cref="WizardClientsViewModel"/> 类的新实例。
/// </summary>
public WizardClientsViewModel()
{
// 实现新增逻辑
AddNodeCommand = new RelayCommand<object>(_ =>
{
// 创建新行对象并添加到集合
var newNode = new ServiceNodeModel
{
ServiceNodeName = "新节点",
ServiceNodeIp = "0.0.0.0",
ServiceNodePort = "5000",
Status = "未检测"
};
ServiceNodes.Add(newNode);
});
// 删除逻辑实现
DeleteNodeCommand = new RelayCommand<ServiceNodeModel>(node =>
{
if (node != null && ServiceNodes.Contains(node))
{
ServiceNodes.Remove(node);
}
});
// 初始化确认命令
ConfirmCommand = new RelayCommand<object>(async _ =>
{
try
{
// 2. 【核心代码】调用通用存储服务
// 泛型 T 自动推断为 ObservableCollection<ServiceNodeModel>
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<object>(async _ => await ExecuteCheckAsync());
// 初始化取消命令
CancelCommand = new RelayCommand<object>(_ =>
{
// 直接触发关闭请求事件,取消并关闭向导
RequestClose?.Invoke();
});
}
#endregion
#region --- IOverlayClosable ---
/// <summary>
/// 当需要关闭蒙板时发生。
/// </summary>
public event Action? RequestClose;
#endregion
#region --- INotifyPropertyChanged ---
/// <summary>
/// 当属性值更改时发生。
/// </summary>
public event PropertyChangedEventHandler? PropertyChanged;
/// <summary>
/// 引发 <see cref="PropertyChanged"/> 事件。
/// </summary>
/// <param name="name">更改的属性名称。</param>
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 = "离线或无设备";
}
}
}
}
}