From d039559402c341d38ef314e2ecd848f48aad0479 Mon Sep 17 00:00:00 2001 From: twice109 <3518499@qq.com> Date: Thu, 1 Jan 2026 22:40:32 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=B7=E5=A4=87=E7=95=8C=E9=9D=A2=E5=9F=BA?= =?UTF-8?q?=E7=A1=80=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SHH.CameraDashboard/App.xaml | 35 +- SHH.CameraDashboard/App.xaml.cs | 36 +- SHH.CameraDashboard/App/AppGlobal.cs | 113 +++ SHH.CameraDashboard/App/AppGlobalData.cs | 17 - SHH.CameraDashboard/App/ThemeManager.cs | 53 -- SHH.CameraDashboard/App/WizardControl.xaml.cs | 94 --- .../Controls/BottomDockControl.xaml | 67 -- .../Controls/BottomDockControl.xaml.cs | 64 -- .../Controls/CameraListControl.xaml | 247 ------- .../Controls/CameraListControl.xaml.cs | 148 ---- .../Controls/DeviceHomeControl.xaml | 199 ------ .../Controls/DeviceHomeControl.xaml.cs | 52 -- .../Controls/DiagnosticControl.xaml | 83 --- .../Controls/DiagnosticControl.xaml.cs | 106 --- .../Converters/BoolToMaxScaleConverter.cs | 29 + .../InverseNullToVisibilityConverter.cs | 19 + .../Converters/NullToVisibilityConverter.cs | 19 + .../Converters/SubscriptionTypeConverter.cs | 34 + SHH.CameraDashboard/Core/AppPaths.cs | 29 + SHH.CameraDashboard/Core/EnumHelper.cs | 21 + SHH.CameraDashboard/Core/IOverlayClosable.cs | 7 + SHH.CameraDashboard/Core/JsonHelper.cs | 109 +++ SHH.CameraDashboard/Core/RelayCommand.cs | 65 ++ SHH.CameraDashboard/Core/ThemeManager.cs | 92 +++ SHH.CameraDashboard/Core/TouchBehavior.cs | 81 +++ SHH.CameraDashboard/MainWindow.xaml | 486 ++++++++----- SHH.CameraDashboard/MainWindow.xaml.cs | 104 +-- SHH.CameraDashboard/MainWindowViewModel.cs | 515 ++++++++++++++ SHH.CameraDashboard/Models/ApiLogEntry.cs | 25 - SHH.CameraDashboard/Models/CameraInfo.cs | 31 - SHH.CameraDashboard/Models/LogWebApiModel.cs | 58 ++ SHH.CameraDashboard/Models/ServerNode.cs | 87 --- .../Models/ServiceNodeModel.cs | 65 ++ .../Models/VideoSourceStatus.cs | 60 -- SHH.CameraDashboard/Pages/CameraEdit.xaml | 321 +++++++++ SHH.CameraDashboard/Pages/CameraEdit.xaml.cs | 15 + .../Pages/CameraImageSubscription.xaml | 317 +++++++++ .../Pages/CameraImageSubscription.xaml.cs | 15 + SHH.CameraDashboard/Pages/CameraImgProc.xaml | 274 +++++++ .../Pages/CameraImgProc.xaml.cs | 14 + SHH.CameraDashboard/Pages/CameraItemTop.xaml | 364 ++++++++++ .../Pages/CameraItemTop.xaml.cs | 55 ++ SHH.CameraDashboard/Pages/CameraList.xaml | 374 ++++++++++ SHH.CameraDashboard/Pages/CameraList.xaml.cs | 17 + SHH.CameraDashboard/Pages/CameraPtz.xaml | 413 +++++++++++ SHH.CameraDashboard/Pages/CameraPtz.xaml.cs | 15 + .../Diagnostics/ServiceNodesDiagnostic.xaml | 126 ++++ .../ServiceNodesDiagnostic.xaml.cs | 15 + .../Diagnostics/ServiceNodesViewModel.cs | 126 ++++ .../WizardClientsControl.xaml} | 64 +- .../Pages/WizardClientsControl.xaml.cs | 19 + .../SHH.CameraDashboard.csproj | 16 +- SHH.CameraDashboard/Services/HttpService.cs | 119 ---- .../Services/LocalStorageService.cs | 110 +++ .../Services/StorageService.cs | 52 -- .../Services/WebApis/ApiClient.cs | 18 + .../WebApis/CameraReps/CameraRepository.cs | 105 +++ .../CameraReps/CameraRepositoryEdit.cs | 396 +++++++++++ .../CameraReps/CameraRepositoryImgProc.cs | 125 ++++ .../CameraRepositoryImgSubscript.cs | 232 ++++++ .../WebApis/CameraReps/CameraRepositoryPtz.cs | 90 +++ .../Services/WebApis/Models/CameraEditInfo.cs | 139 ++++ .../WebApis}/Models/DeviceBrand.cs | 26 +- .../Services/WebApis/Models/PtzAction.cs | 20 + .../Services/WebApis/Models/PtzControlDto.cs | 20 + .../WebApis/Models/WebApiCameraModel.cs | 181 +++++ .../Services/WebApis/MonitorRepository.cs | 38 + .../Services/WebApis/Routes.cs | 34 + .../Services/WebApis/WebApiService.cs | 299 ++++++++ .../Style/Themes/Colors.Dark.xaml | 13 + SHH.CameraDashboard/Style/Themes/Icons.xaml | 133 ++++ SHH.CameraDashboard/Style/Themes/Styles.xaml | 670 ++++++++++++++++-- .../ViewModels/CameraEditViewModel.cs | 177 +++++ .../CameraImageSubscriptionViewModels.cs | 253 +++++++ .../ViewModels/CameraImgProcViewModel.cs | 369 ++++++++++ .../ViewModels/CameraItemTopViewModel.cs | 145 ++++ .../ViewModels/CameraListViewModel.cs | 259 +++++++ .../ViewModels/CameraPtzViewModel.cs | 133 ++++ .../ViewModels/WizardClientsVewModel.cs | 188 +++++ .../Abstractions/Enums/DeviceBrand.cs | 26 +- .../Controllers/CamerasController.cs | 58 ++ 81 files changed, 8333 insertions(+), 1905 deletions(-) create mode 100644 SHH.CameraDashboard/App/AppGlobal.cs delete mode 100644 SHH.CameraDashboard/App/AppGlobalData.cs delete mode 100644 SHH.CameraDashboard/App/ThemeManager.cs delete mode 100644 SHH.CameraDashboard/App/WizardControl.xaml.cs delete mode 100644 SHH.CameraDashboard/Controls/BottomDockControl.xaml delete mode 100644 SHH.CameraDashboard/Controls/BottomDockControl.xaml.cs delete mode 100644 SHH.CameraDashboard/Controls/CameraListControl.xaml delete mode 100644 SHH.CameraDashboard/Controls/CameraListControl.xaml.cs delete mode 100644 SHH.CameraDashboard/Controls/DeviceHomeControl.xaml delete mode 100644 SHH.CameraDashboard/Controls/DeviceHomeControl.xaml.cs delete mode 100644 SHH.CameraDashboard/Controls/DiagnosticControl.xaml delete mode 100644 SHH.CameraDashboard/Controls/DiagnosticControl.xaml.cs create mode 100644 SHH.CameraDashboard/Converters/BoolToMaxScaleConverter.cs create mode 100644 SHH.CameraDashboard/Converters/InverseNullToVisibilityConverter.cs create mode 100644 SHH.CameraDashboard/Converters/NullToVisibilityConverter.cs create mode 100644 SHH.CameraDashboard/Converters/SubscriptionTypeConverter.cs create mode 100644 SHH.CameraDashboard/Core/AppPaths.cs create mode 100644 SHH.CameraDashboard/Core/EnumHelper.cs create mode 100644 SHH.CameraDashboard/Core/IOverlayClosable.cs create mode 100644 SHH.CameraDashboard/Core/JsonHelper.cs create mode 100644 SHH.CameraDashboard/Core/RelayCommand.cs create mode 100644 SHH.CameraDashboard/Core/ThemeManager.cs create mode 100644 SHH.CameraDashboard/Core/TouchBehavior.cs create mode 100644 SHH.CameraDashboard/MainWindowViewModel.cs delete mode 100644 SHH.CameraDashboard/Models/ApiLogEntry.cs delete mode 100644 SHH.CameraDashboard/Models/CameraInfo.cs create mode 100644 SHH.CameraDashboard/Models/LogWebApiModel.cs delete mode 100644 SHH.CameraDashboard/Models/ServerNode.cs create mode 100644 SHH.CameraDashboard/Models/ServiceNodeModel.cs delete mode 100644 SHH.CameraDashboard/Models/VideoSourceStatus.cs create mode 100644 SHH.CameraDashboard/Pages/CameraEdit.xaml create mode 100644 SHH.CameraDashboard/Pages/CameraEdit.xaml.cs create mode 100644 SHH.CameraDashboard/Pages/CameraImageSubscription.xaml create mode 100644 SHH.CameraDashboard/Pages/CameraImageSubscription.xaml.cs create mode 100644 SHH.CameraDashboard/Pages/CameraImgProc.xaml create mode 100644 SHH.CameraDashboard/Pages/CameraImgProc.xaml.cs create mode 100644 SHH.CameraDashboard/Pages/CameraItemTop.xaml create mode 100644 SHH.CameraDashboard/Pages/CameraItemTop.xaml.cs create mode 100644 SHH.CameraDashboard/Pages/CameraList.xaml create mode 100644 SHH.CameraDashboard/Pages/CameraList.xaml.cs create mode 100644 SHH.CameraDashboard/Pages/CameraPtz.xaml create mode 100644 SHH.CameraDashboard/Pages/CameraPtz.xaml.cs create mode 100644 SHH.CameraDashboard/Pages/Diagnostics/ServiceNodesDiagnostic.xaml create mode 100644 SHH.CameraDashboard/Pages/Diagnostics/ServiceNodesDiagnostic.xaml.cs create mode 100644 SHH.CameraDashboard/Pages/Diagnostics/ServiceNodesViewModel.cs rename SHH.CameraDashboard/{App/WizardControl.xaml => Pages/WizardClientsControl.xaml} (64%) create mode 100644 SHH.CameraDashboard/Pages/WizardClientsControl.xaml.cs delete mode 100644 SHH.CameraDashboard/Services/HttpService.cs create mode 100644 SHH.CameraDashboard/Services/LocalStorageService.cs delete mode 100644 SHH.CameraDashboard/Services/StorageService.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/ApiClient.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/CameraReps/CameraRepository.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/CameraReps/CameraRepositoryEdit.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/CameraReps/CameraRepositoryImgProc.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/CameraReps/CameraRepositoryImgSubscript.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/CameraReps/CameraRepositoryPtz.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/Models/CameraEditInfo.cs rename SHH.CameraDashboard/{ => Services/WebApis}/Models/DeviceBrand.cs (81%) create mode 100644 SHH.CameraDashboard/Services/WebApis/Models/PtzAction.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/Models/PtzControlDto.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/Models/WebApiCameraModel.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/MonitorRepository.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/Routes.cs create mode 100644 SHH.CameraDashboard/Services/WebApis/WebApiService.cs create mode 100644 SHH.CameraDashboard/Style/Themes/Icons.xaml create mode 100644 SHH.CameraDashboard/ViewModels/CameraEditViewModel.cs create mode 100644 SHH.CameraDashboard/ViewModels/CameraImageSubscriptionViewModels.cs create mode 100644 SHH.CameraDashboard/ViewModels/CameraImgProcViewModel.cs create mode 100644 SHH.CameraDashboard/ViewModels/CameraItemTopViewModel.cs create mode 100644 SHH.CameraDashboard/ViewModels/CameraListViewModel.cs create mode 100644 SHH.CameraDashboard/ViewModels/CameraPtzViewModel.cs create mode 100644 SHH.CameraDashboard/ViewModels/WizardClientsVewModel.cs diff --git a/SHH.CameraDashboard/App.xaml b/SHH.CameraDashboard/App.xaml index 6998269..2957226 100644 --- a/SHH.CameraDashboard/App.xaml +++ b/SHH.CameraDashboard/App.xaml @@ -1,25 +1,22 @@ - + - - - - - - - - - - - - - + + + + + + + + + - + \ No newline at end of file diff --git a/SHH.CameraDashboard/App.xaml.cs b/SHH.CameraDashboard/App.xaml.cs index f60e5be..613b685 100644 --- a/SHH.CameraDashboard/App.xaml.cs +++ b/SHH.CameraDashboard/App.xaml.cs @@ -1,5 +1,4 @@ -using System.Configuration; -using System.Data; +using System.Collections.ObjectModel; using System.Windows; namespace SHH.CameraDashboard @@ -9,6 +8,35 @@ namespace SHH.CameraDashboard /// public partial class App : Application { - } + protected override async void OnStartup(StartupEventArgs e) + { + base.OnStartup(e); -} + // 1. 【核心代码】程序启动时,异步读取配置文件 + var savedNodes = await LocalStorageService.LoadAsync>(AppPaths.ServiceNodesConfig); + if (savedNodes != null) + { + foreach (var node in savedNodes) + AppGlobal.ServiceNodes.Add(node); + } + + // 3. 启动主窗口 + // 注意:如果 LoadAsync 耗时较长,这里可能会导致启动画面停留, + // 实际项目中可以搞一个 Splash Screen (启动屏) 来做这件事。 + var mainWin = new MainWindow(); + mainWin.Show(); + } + + /// + /// 全局统一退出入口 + /// + public static void ShutdownApp() + { + // 1. 这里可以处理统一的资源清理逻辑 (如停止摄像头推流、关闭数据库连接) + // 2. 保存用户配置 + // 3. 彻底退出 + Current.Shutdown(); + } + + } +} \ No newline at end of file diff --git a/SHH.CameraDashboard/App/AppGlobal.cs b/SHH.CameraDashboard/App/AppGlobal.cs new file mode 100644 index 0000000..94d491d --- /dev/null +++ b/SHH.CameraDashboard/App/AppGlobal.cs @@ -0,0 +1,113 @@ +using System.Collections.ObjectModel; + +namespace SHH.CameraDashboard +{ + /// + /// 应用程序全局状态和事件总线。 + /// 此类作为一个静态的中央枢纽,用于在应用程序的不同部分之间共享数据和通信。 + /// + public static class AppGlobal + { + #region --- 全局数据存储 --- + + /// + /// 获取一个可观察的集合,用于存储和显示所有已配置的服务节点。 + /// 由于使用了 ,当集合内容发生变化时,UI(如 ListView)会自动更新。 + /// + public static ObservableCollection ServiceNodes { get; } + = new ObservableCollection(); + + /// + /// 获取或设置当前正在使用的服务节点。 + /// 当用户从列表中选择一个节点时,应更新此属性。 + /// + public static ServiceNodeModel? UseServiceNode { get; set; } + + #endregion + + #region --- 全局事件总线 --- + + #region CameraAdd + + /// + /// 当应用程序的任何部分请求添加一个新摄像头时发生。 + /// + public static event Action? OnRequestAddCamera; + + /// + /// 触发 事件,以请求打开摄像头添加界面。 + /// + public static void RequestAdd() + => OnRequestAddCamera?.Invoke(); + + #endregion + + #region CameraEdit + + /// + /// 当应用程序的任何部分请求编辑一个摄像头时发生。 + /// 事件处理程序将接收到要编辑的 实例。 + /// + public static event Action? OnRequestEditCamera; + + /// + /// 触发 事件,以请求打开摄像头编辑界面。 + /// + /// 要编辑的摄像头数据模型。 + public static void RequestEdit(WebApiCameraModel camera) + => OnRequestEditCamera?.Invoke(camera); + + #endregion + + #region CameraDelete + + /// + /// 当应用程序的任何部分请求删除一个摄像头时发生。 + /// 事件处理程序将接收到要删除的 实例。 + /// + public static event Action? OnRequestDeleteCamera; + + /// + /// 触发 事件,以请求删除指定的摄像头。 + /// + /// 要删除的摄像头数据模型。 + public static void RequestDelete(WebApiCameraModel camera) + => OnRequestDeleteCamera?.Invoke(camera); + + #endregion + + #region CameraRefreshList + + /// + /// 当应用程序的任何部分请求刷新摄像头列表时发生。 + /// + public static event Action? OnRefreshListRequest; + + /// + /// 触发 事件,以请求刷新摄像头列表数据。 + /// + public static void RequestRefresh() + => OnRefreshListRequest?.Invoke(); + + #endregion + + // [新增] 请求云台控制事件 + public static event Action? OnRequestPtzCamera; + + // [新增] 触发方法 + public static void RequestPtz(WebApiCameraModel camera) => OnRequestPtzCamera?.Invoke(camera); + + // 图像处理 + public static event Action? OnRequestImgProc; + + public static void RequestImgProc(WebApiCameraModel camera) => OnRequestImgProc?.Invoke(camera); + + // 1. 定义事件委托:当 ViewModel 请求订阅时触发 + public static event Action? OnRequestSubscription; + + // 2. 定义触发方法:供 CameraItemTopViewModel 调用 + public static void RequestSubscription(WebApiCameraModel camera) => OnRequestSubscription?.Invoke(camera); + + #endregion + } +} \ No newline at end of file diff --git a/SHH.CameraDashboard/App/AppGlobalData.cs b/SHH.CameraDashboard/App/AppGlobalData.cs deleted file mode 100644 index ca201f8..0000000 --- a/SHH.CameraDashboard/App/AppGlobalData.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace SHH.CameraDashboard -{ - // 2. 全局配置存储 - public static class AppGlobalData - { - public static List ActiveServerList { get; private set; } = new List(); - - public static void SaveConfig(IEnumerable nodes) - { - ActiveServerList.Clear(); - foreach (var node in nodes) - { - ActiveServerList.Add(new ServerNode { Ip = node.Ip, Port = node.Port }); - } - } - } -} \ No newline at end of file diff --git a/SHH.CameraDashboard/App/ThemeManager.cs b/SHH.CameraDashboard/App/ThemeManager.cs deleted file mode 100644 index b0c24ef..0000000 --- a/SHH.CameraDashboard/App/ThemeManager.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; - -namespace SHH.CameraDashboard -{ - public enum ThemeType { Dark, Light } - - public static class ThemeManager - { - public static void ChangeTheme(ThemeType theme) - { - var appResources = Application.Current.Resources; - - // 1. 找到旧的颜色字典并移除 - // 我们通过检查 Source 路径来识别它 - ResourceDictionary oldDict = null; - foreach (var dict in appResources.MergedDictionaries) - { - // 只要路径里包含 "Colors." 说明它是我们的皮肤文件 - if (dict.Source != null && dict.Source.OriginalString.Contains("Themes/Colors.")) - { - oldDict = dict; - break; - } - } - - if (oldDict != null) - { - appResources.MergedDictionaries.Remove(oldDict); - } - - // 2. 加载新字典 - string dictName = theme switch - { - ThemeType.Light => "/Style/Themes/Colors.Light.xaml", - ThemeType.Dark => "/Style/Themes/Colors.Dark.xaml", - _ => "/Style/Themes/Colors.Dark.xaml" // 默认 - }; - - var newDict = new ResourceDictionary - { - Source = new Uri(dictName, UriKind.Relative) - }; - - // 3. 添加到集合中 (建议加在最前面,或者根据索引位置) - appResources.MergedDictionaries.Add(newDict); - } - } -} diff --git a/SHH.CameraDashboard/App/WizardControl.xaml.cs b/SHH.CameraDashboard/App/WizardControl.xaml.cs deleted file mode 100644 index b96bcb8..0000000 --- a/SHH.CameraDashboard/App/WizardControl.xaml.cs +++ /dev/null @@ -1,94 +0,0 @@ -using SHH.CameraDashboard.Services; -using System.Collections.ObjectModel; -using System.Windows; -using System.Windows.Controls; -using System.Xml.Linq; - -namespace SHH.CameraDashboard -{ - public partial class WizardControl : UserControl - { - private const string ServerConfigFile = "servers.config.json"; - - // 绑定源 - public ObservableCollection Nodes { get; set; } = new ObservableCollection(); - - // 定义关闭事件,通知主窗体关闭模态框 - public event EventHandler RequestClose; - - public WizardControl() - { - InitializeComponent(); - - // 使用泛型加载,指定返回类型为 ObservableCollection - // 这里的 LoadServers 逻辑变为了通用的 Load - Nodes = StorageService.Load>(ServerConfigFile); - - NodeList.ItemsSource = Nodes; - } - - private void AddNode_Click(object sender, RoutedEventArgs e) - { - Nodes.Add(new ServerNode()); - } - - private void DeleteNode_Click(object sender, RoutedEventArgs e) - { - if (sender is Button btn && btn.DataContext is ServerNode node) - { - Nodes.Remove(node); - } - } - - private async void Check_Click(object sender, RoutedEventArgs e) - { - // 禁用按钮防止重复点击 - var btn = sender as Button; - if (btn != null) btn.IsEnabled = false; - - foreach (var node in Nodes) - { - node.SetResult(false, "⏳ 检测中..."); - - // 构造 URL - string url = $"http://{node.Ip}:{node.Port}/api/Cameras"; - // 或者是 /api/health,看你的后端提供什么接口 - - try - { - // 【修改】这里调用封装好的 HttpService - // 我们使用 TestConnectionAsync,它内部会触发 OnApiLog 事件记录日志 - bool isConnected = await HttpService.TestConnectionAsync(url); - - if (isConnected) - node.SetResult(true, "✅ 连接成功"); - else - node.SetResult(false, "❌ 状态码异常"); - } - catch (Exception ex) - { - // 异常也被 HttpService 记录了,这里只负责更新 UI 状态 - node.SetResult(false, "❌ 无法连接"); - } - } - - if (btn != null) btn.IsEnabled = true; - } - - private void Apply_Click(object sender, RoutedEventArgs e) - { - // 将当前的 Nodes 集合保存到指定文件 - StorageService.Save(Nodes, ServerConfigFile); - - // 同步到全局单例内存中 - AppGlobalData.SaveConfig(Nodes); - - RequestClose?.Invoke(this, EventArgs.Empty); - } - - private void Cancel_Click(object sender, RoutedEventArgs e) - { - RequestClose?.Invoke(this, EventArgs.Empty); - } - } -} \ No newline at end of file diff --git a/SHH.CameraDashboard/Controls/BottomDockControl.xaml b/SHH.CameraDashboard/Controls/BottomDockControl.xaml deleted file mode 100644 index f8ef694..0000000 --- a/SHH.CameraDashboard/Controls/BottomDockControl.xaml +++ /dev/null @@ -1,67 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SHH.CameraDashboard/Controls/BottomDockControl.xaml.cs b/SHH.CameraDashboard/Controls/BottomDockControl.xaml.cs deleted file mode 100644 index bef341b..0000000 --- a/SHH.CameraDashboard/Controls/BottomDockControl.xaml.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Windows; -using System.Windows.Controls; -using System.Windows.Media; - -namespace SHH.CameraDashboard -{ - public partial class BottomDockControl : UserControl - { - private bool _isExpanded = false; - - public BottomDockControl() - { - InitializeComponent(); - - // 核心修正:这里必须使用 WebApiDiag,因为它对应你 XAML 里的 x:Name - if (this.WebApiDiag != null) - { - // 订阅 DiagnosticControl 抛出的关闭事件 - this.WebApiDiag.RequestCollapse += (s, e) => - { - // 当子页面点击“关闭”按钮时,执行收回面板的方法 - HideExpandedPanel(); - }; - } - } - - // 逻辑:隐藏上方的大面板 - private void HideExpandedPanel() - { - ExpandedPanel.Visibility = Visibility.Collapsed; - ArrowIcon.Text = "▲"; // 箭头恢复向上 - } - - // 接收全局日志,分发给内部控件,并更新状态栏摘要 - public void PushLog(ApiLogEntry log) - { - // 1. 推送给内部的诊断控件 (详细列表) - WebApiDiag.PushLog(log); - - // 2. 更新底部状态栏 (摘要) - string statusIcon = log.IsSuccess ? "✅" : "❌"; - LatestLogText.Text = $"{statusIcon} [{log.Time:HH:mm:ss}] {log.Method} {log.Url} ({log.StatusCode})"; - LatencyText.Text = $"{log.DurationMs}ms"; - - // 如果失败,可以将状态栏背景变红一下(可选) - if (!log.IsSuccess) - { - // 这里简单处理,如果想要复杂的动画可以使用 Storyboard - LatestLogText.Foreground = new SolidColorBrush(Colors.Yellow); - } - else - { - LatestLogText.Foreground = Brushes.White; - } - } - - private void TogglePanel_Click(object sender, System.Windows.Input.MouseButtonEventArgs e) - { - _isExpanded = !_isExpanded; - ExpandedPanel.Visibility = _isExpanded ? Visibility.Visible : Visibility.Collapsed; - ArrowIcon.Text = _isExpanded ? "▼" : "▲"; - } - } -} \ No newline at end of file diff --git a/SHH.CameraDashboard/Controls/CameraListControl.xaml b/SHH.CameraDashboard/Controls/CameraListControl.xaml deleted file mode 100644 index 94f8ddc..0000000 --- a/SHH.CameraDashboard/Controls/CameraListControl.xaml +++ /dev/null @@ -1,247 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/SHH.CameraDashboard/Controls/CameraListControl.xaml.cs b/SHH.CameraDashboard/Controls/CameraListControl.xaml.cs deleted file mode 100644 index bb10155..0000000 --- a/SHH.CameraDashboard/Controls/CameraListControl.xaml.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System.Collections.ObjectModel; -using System.Windows; -using System.Windows.Controls; - -namespace SHH.CameraDashboard -{ - // 简单的包装类,用于 ComboBox 显示 - public class ServerOption - { - public string Name { get; set; } - - public string Ip { get; set; } - public int Port { get; set; } - // 修改显示属性:如果有名字就显示 名字(IP),没有就显示 IP - public string DisplayText => string.IsNullOrEmpty(Name) ? $"{Ip}:{Port}" : $"{Name} ({Ip})"; - } - - public partial class CameraListControl : UserControl - { - // 所有摄像头数据(原始全集) - private List _allCameras = new List(); - - // 绑定到界面的数据(过滤后) - public ObservableCollection DisplayCameras { get; set; } = new ObservableCollection(); - - public CameraListControl() - { - InitializeComponent(); - CameraList.ItemsSource = DisplayCameras; - - // 初始加载服务器列表 - ReloadServers(); - } - - /// - /// 公开方法:供主窗体在向导结束后调用,刷新下拉框 - /// - public void ReloadServers() - { - var savedSelection = ServerCombo.SelectedItem as ServerOption; - - // 1. 转换全局配置到 ComboBox 选项 - var options = AppGlobalData.ActiveServerList - .Select(n => new ServerOption { Ip = n.Ip, Port = n.Port }) - .ToList(); - - ServerCombo.ItemsSource = options; - - // 2. 尝试恢复之前的选中项,或者默认选中第一个 - if (options.Count > 0) - { - if (savedSelection != null) - { - var match = options.FirstOrDefault(o => o.Ip == savedSelection.Ip && o.Port == savedSelection.Port); - ServerCombo.SelectedItem = match ?? options[0]; - } - else - { - ServerCombo.SelectedItem = options[0]; - } - } - else - { - // 如果没有配置,清空列表 - _allCameras.Clear(); - DisplayCameras.Clear(); - UpdateEmptyState(); - } - } - - private async void ServerCombo_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (ServerCombo.SelectedItem is ServerOption server) - { - // 切换服务器,加载数据 - LoadingMask.Visibility = Visibility.Visible; - EmptyText.Visibility = Visibility.Collapsed; - - string url = $"http://{server.Ip}:{server.Port}/api/Cameras"; - - try - { - // 使用 HttpService 获取列表 - var list = await HttpService.GetAsync>(url); - - _allCameras = list ?? new List(); - - // 应用当前的搜索词 - FilterList(SearchBox.Text); - } - catch - { - // 失败清空 - _allCameras.Clear(); - DisplayCameras.Clear(); - } - finally - { - LoadingMask.Visibility = Visibility.Collapsed; - UpdateEmptyState(); - } - } - } - - private void SearchBox_TextChanged(object sender, TextChangedEventArgs e) - { - FilterList(SearchBox.Text); - } - - private void FilterList(string keyword) - { - DisplayCameras.Clear(); - - if (string.IsNullOrWhiteSpace(keyword)) - { - foreach (var c in _allCameras) DisplayCameras.Add(c); - } - else - { - var lowerKw = keyword.ToLower(); - var results = _allCameras.Where(c => - (c.Name != null && c.Name.ToLower().Contains(lowerKw)) || - (c.IpAddress != null && c.IpAddress.Contains(lowerKw)) - ); - - foreach (var c in results) DisplayCameras.Add(c); - } - } - - private void UpdateEmptyState() - { - EmptyText.Visibility = DisplayCameras.Count == 0 ? Visibility.Visible : Visibility.Collapsed; - } - - // 1. 定义一个事件:当设备被选中时触发 - public event System.Action OnDeviceSelected; - - // 2. 实现 ListView 的选中事件处理 - private void CameraList_SelectionChanged(object sender, SelectionChangedEventArgs e) - { - if (CameraList.SelectedItem is CameraInfo selectedCam) - { - // 触发事件,把选中的相机传出去 - OnDeviceSelected?.Invoke(selectedCam); - } - } - } -} \ No newline at end of file diff --git a/SHH.CameraDashboard/Controls/DeviceHomeControl.xaml b/SHH.CameraDashboard/Controls/DeviceHomeControl.xaml deleted file mode 100644 index 5abdfdc..0000000 --- a/SHH.CameraDashboard/Controls/DeviceHomeControl.xaml +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -