Files
Ayay/SHH.CameraDashboard/ViewModels/WizardClientsVewModel.cs
2026-01-01 22:40:32 +08:00

188 lines
6.2 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 = "离线或无设备";
}
}
}
}
}