具备界面基础功能

This commit is contained in:
2026-01-01 22:40:32 +08:00
parent 0c86b4dad3
commit d039559402
81 changed files with 8333 additions and 1905 deletions

View File

@@ -0,0 +1,177 @@
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必须大于0IP不能为空
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();
}
}
}