177 lines
6.1 KiB
C#
177 lines
6.1 KiB
C#
|
|
using System.ComponentModel;
|
|||
|
|
using System.ComponentModel.DataAnnotations;
|
|||
|
|
using System.Runtime.CompilerServices;
|
|||
|
|
using System.Windows;
|
|||
|
|
using System.Windows.Input;
|
|||
|
|
|
|||
|
|
namespace SHH.CameraDashboard
|
|||
|
|
{
|
|||
|
|
public class CameraEditViewModel : INotifyPropertyChanged
|
|||
|
|
{
|
|||
|
|
// 编辑中的数据副本
|
|||
|
|
private CameraEditInfo _editingDto;
|
|||
|
|
|
|||
|
|
// 定义一个事件,通知 View 关闭窗口(或者 UserControl 所在的宿主)
|
|||
|
|
public event Action<bool, CameraEditInfo> RequestClose;
|
|||
|
|
|
|||
|
|
public CameraEditInfo EditingDto
|
|||
|
|
{
|
|||
|
|
get => _editingDto;
|
|||
|
|
set { _editingDto = value; OnPropertyChanged(); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public ICommand SaveCommand { get; }
|
|||
|
|
public ICommand CancelCommand { get; }
|
|||
|
|
|
|||
|
|
private bool _isAdd = false;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 构造函数
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="sourceDto">如果是编辑模式,传入源对象;如果是新增,传入 null</param>
|
|||
|
|
public CameraEditViewModel(CameraEditInfo sourceDto = null)
|
|||
|
|
{
|
|||
|
|
// 初始化枚举列表
|
|||
|
|
InitBrandOptions();
|
|||
|
|
|
|||
|
|
if (sourceDto != null)
|
|||
|
|
{
|
|||
|
|
// 编辑模式:深拷贝数据,避免直接修改源对象
|
|||
|
|
EditingDto = CloneDto(sourceDto);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 新增模式:创建默认对象
|
|||
|
|
EditingDto = new CameraEditInfo
|
|||
|
|
{
|
|||
|
|
Name = "新设备",
|
|||
|
|
Port = 8000,
|
|||
|
|
ChannelIndex = 1
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (EditingDto.Id == 0)
|
|||
|
|
_isAdd = true;
|
|||
|
|
|
|||
|
|
SaveCommand = new RelayCommand(ExecuteSave, CanSave);
|
|||
|
|
CancelCommand = new RelayCommand(ExecuteCancel);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool CanSave(object obj)
|
|||
|
|
{
|
|||
|
|
// 简单验证:ID必须大于0,IP不能为空
|
|||
|
|
if (EditingDto == null) return false;
|
|||
|
|
return EditingDto.Id > 0 && !string.IsNullOrWhiteSpace(EditingDto.IpAddress);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool IsSaving;
|
|||
|
|
|
|||
|
|
private async void ExecuteSave(object obj)
|
|||
|
|
{
|
|||
|
|
if (IsSaving) return;
|
|||
|
|
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
IsSaving = true; // 开启 Loading
|
|||
|
|
|
|||
|
|
bool isSuccess = false;
|
|||
|
|
|
|||
|
|
if (_isAdd)
|
|||
|
|
{
|
|||
|
|
isSuccess = await ApiClient.Instance.Cameras.CreateCameraAsync(EditingDto, "摄像头-新增");
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
isSuccess = await ApiClient.Instance.Cameras.UpdateCameraAsync(EditingDto, "摄像头-编辑");
|
|||
|
|
_isAdd = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 1. 调用 Repository (ApiClient)
|
|||
|
|
// 注意:这里调用的是我们刚才在 Repository 中定义的 UpdateCameraAsync
|
|||
|
|
if (isSuccess)
|
|||
|
|
{
|
|||
|
|
// 2. 成功:通知外部关闭面板,并刷新列表
|
|||
|
|
RequestClose?.Invoke(true, EditingDto);
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
// 3. 失败:弹窗提示
|
|||
|
|
MessageBox.Show("保存失败,请检查服务节点日志。", "系统提示", MessageBoxButton.OK, MessageBoxImage.Error);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
MessageBox.Show($"保存过程中发生异常:\n{ex.Message}", "系统错误", MessageBoxButton.OK, MessageBoxImage.Error);
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
IsSaving = false; // 关闭 Loading
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private void ExecuteCancel(object obj)
|
|||
|
|
{
|
|||
|
|
// false 表示取消操作
|
|||
|
|
RequestClose?.Invoke(false, null);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 手动深拷贝 DTO,防止引用传递导致界面未点保存就修改了列表
|
|||
|
|
/// 生产环境建议用 AutoMapper 或 JSON 序列化实现
|
|||
|
|
/// </summary>
|
|||
|
|
private CameraEditInfo CloneDto(CameraEditInfo source)
|
|||
|
|
{
|
|||
|
|
return new CameraEditInfo
|
|||
|
|
{
|
|||
|
|
Id = source.Id,
|
|||
|
|
Name = source.Name,
|
|||
|
|
Brand = source.Brand,
|
|||
|
|
Location = source.Location,
|
|||
|
|
IpAddress = source.IpAddress,
|
|||
|
|
Port = source.Port,
|
|||
|
|
Username = source.Username,
|
|||
|
|
Password = source.Password,
|
|||
|
|
RenderHandle = source.RenderHandle,
|
|||
|
|
ChannelIndex = source.ChannelIndex,
|
|||
|
|
RtspPath = source.RtspPath,
|
|||
|
|
MainboardIp = source.MainboardIp,
|
|||
|
|
MainboardPort = source.MainboardPort,
|
|||
|
|
StreamType = source.StreamType,
|
|||
|
|
UseGrayscale = source.UseGrayscale,
|
|||
|
|
EnhanceImage = source.EnhanceImage,
|
|||
|
|
AllowCompress = source.AllowCompress,
|
|||
|
|
AllowExpand = source.AllowExpand,
|
|||
|
|
TargetResolution = source.TargetResolution
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
public event PropertyChangedEventHandler? PropertyChanged;
|
|||
|
|
protected void OnPropertyChanged([CallerMemberName] string name = null)
|
|||
|
|
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
|
|||
|
|
|
|||
|
|
// 1. 定义一个简单的内部类或结构,用于 ComboBox 显示
|
|||
|
|
public class BrandOption
|
|||
|
|
{
|
|||
|
|
public string Label { get; set; } // 显示的文本 (Description)
|
|||
|
|
public int Value { get; set; } // 实际的值 (int)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 数据源属性
|
|||
|
|
public List<BrandOption> BrandOptions { get; private set; }
|
|||
|
|
|
|||
|
|
private void InitBrandOptions()
|
|||
|
|
{
|
|||
|
|
// 遍历 DeviceBrand 枚举的所有值
|
|||
|
|
BrandOptions = Enum.GetValues(typeof(DeviceBrand))
|
|||
|
|
.Cast<DeviceBrand>()
|
|||
|
|
.Select(e => new BrandOption
|
|||
|
|
{
|
|||
|
|
// 获取 Description ("海康威视")
|
|||
|
|
Label = EnumHelper.GetDescription(e),
|
|||
|
|
// 获取 int 值 (1)
|
|||
|
|
Value = (int)e
|
|||
|
|
})
|
|||
|
|
.ToList();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|