From 92b38fc656490aa4ce2af6abdd343c7393aa5dca Mon Sep 17 00:00:00 2001 From: Administrator Date: Thu, 18 Dec 2025 00:43:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=92=8C=E6=96=B0=E8=AE=A2=E5=8D=95=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 每30分钟自动增量同步订单 - 新订单到达时播放提示音、任务栏闪烁 - 窗口不在前台时弹出Toast通知(5秒后自动关闭) - 界面右上角显示自动同步开关和倒计时(每秒更新) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../ViewModels/OrderListViewModel.cs | 281 +++++++++++++++++- PackagingMallShipper/Views/OrderListView.xaml | 39 ++- 2 files changed, 310 insertions(+), 10 deletions(-) diff --git a/PackagingMallShipper/ViewModels/OrderListViewModel.cs b/PackagingMallShipper/ViewModels/OrderListViewModel.cs index 8c1e606..9e5ebf8 100644 --- a/PackagingMallShipper/ViewModels/OrderListViewModel.cs +++ b/PackagingMallShipper/ViewModels/OrderListViewModel.cs @@ -1,8 +1,10 @@ using System; using System.Collections.ObjectModel; using System.Linq; +using System.Threading; using System.Threading.Tasks; using System.Windows; +using System.Windows.Threading; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Microsoft.Win32; @@ -19,6 +21,9 @@ namespace PackagingMallShipper.ViewModels private readonly IShipService _shipService; private readonly IExcelService _excelService; + private DispatcherTimer _autoSyncTimer; + private const int AutoSyncIntervalMinutes = 30; + [ObservableProperty] private ObservableCollection _orders = new ObservableCollection(); @@ -40,6 +45,15 @@ namespace PackagingMallShipper.ViewModels [ObservableProperty] private string _syncProgress = ""; + [ObservableProperty] + private bool _autoSyncEnabled = true; + + [ObservableProperty] + private string _nextSyncTime = ""; + + [ObservableProperty] + private DateTime? _lastSyncTime; + public OrderListViewModel( IOrderService orderService, ISyncService syncService, @@ -65,12 +79,257 @@ namespace PackagingMallShipper.ViewModels { StatusMessage = $"发货中 {current}/{total}"; }; + + // 初始化自动同步定时器 + InitializeAutoSyncTimer(); + } + + private void InitializeAutoSyncTimer() + { + _autoSyncTimer = new DispatcherTimer + { + Interval = TimeSpan.FromMinutes(AutoSyncIntervalMinutes) + }; + _autoSyncTimer.Tick += async (s, e) => await AutoSyncAsync(); + + // 更新下次同步时间显示的定时器(每秒更新) + var updateTimer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(1) + }; + updateTimer.Tick += (s, e) => UpdateNextSyncTimeDisplay(); + updateTimer.Start(); } public async Task InitializeAsync() { await RefreshOrdersAsync(); await UpdateCountsAsync(); + + // 启动自动同步 + StartAutoSync(); + } + + public void StartAutoSync() + { + if (_autoSyncTimer != null && AutoSyncEnabled) + { + _autoSyncTimer.Start(); + LastSyncTime = DateTime.Now; + UpdateNextSyncTimeDisplay(); + StatusMessage = $"自动同步已启动,每 {AutoSyncIntervalMinutes} 分钟同步一次"; + } + } + + public void StopAutoSync() + { + _autoSyncTimer?.Stop(); + NextSyncTime = "已停止"; + } + + private void UpdateNextSyncTimeDisplay() + { + if (!AutoSyncEnabled || LastSyncTime == null) + { + NextSyncTime = "已停止"; + return; + } + + var nextSync = LastSyncTime.Value.AddMinutes(AutoSyncIntervalMinutes); + var remaining = nextSync - DateTime.Now; + + if (remaining.TotalSeconds > 0) + { + NextSyncTime = $"{(int)remaining.TotalMinutes}分{remaining.Seconds}秒后同步"; + } + else + { + NextSyncTime = "同步中..."; + } + } + + private async Task AutoSyncAsync() + { + if (IsBusy || !AutoSyncEnabled) return; + + try + { + var previousPendingCount = PendingCount; + + IsBusy = true; + StatusMessage = "自动同步中..."; + + var result = await _syncService.SyncOrdersAsync(SyncMode.Incremental); + LastSyncTime = DateTime.Now; + + await RefreshOrdersAsync(); + await UpdateCountsAsync(); + + // 检查是否有新订单 + if (result.NewCount > 0) + { + // 发送系统通知 + ShowNewOrderNotification(result.NewCount); + } + + StatusMessage = $"自动同步完成 - 新增 {result.NewCount},更新 {result.UpdatedCount} ({DateTime.Now:HH:mm:ss})"; + } + catch (UnauthorizedAccessException) + { + StatusMessage = "自动同步失败: 登录已过期"; + StopAutoSync(); + } + catch (Exception ex) + { + StatusMessage = $"自动同步失败: {ex.Message}"; + } + finally + { + IsBusy = false; + SyncProgress = ""; + UpdateNextSyncTimeDisplay(); + } + } + + private void ShowNewOrderNotification(int newOrderCount) + { + // 使用 Windows 系统托盘通知 + Application.Current.Dispatcher.Invoke(() => + { + var mainWindow = Application.Current.MainWindow; + + // 如果窗口最小化或不在前台,显示通知 + if (mainWindow != null) + { + // 闪烁任务栏 + FlashWindow(mainWindow); + + // 播放系统提示音 + System.Media.SystemSounds.Exclamation.Play(); + + // 如果窗口不在前台,弹出消息提示 + if (!mainWindow.IsActive) + { + // 使用 Toast 样式的通知 + var notification = new Window + { + Title = "新订单通知", + Width = 300, + Height = 120, + WindowStyle = WindowStyle.ToolWindow, + Topmost = true, + ShowInTaskbar = false, + WindowStartupLocation = WindowStartupLocation.Manual, + ResizeMode = ResizeMode.NoResize + }; + + // 定位到屏幕右下角 + var workArea = SystemParameters.WorkArea; + notification.Left = workArea.Right - notification.Width - 10; + notification.Top = workArea.Bottom - notification.Height - 10; + + var content = new System.Windows.Controls.StackPanel + { + Margin = new Thickness(15), + VerticalAlignment = VerticalAlignment.Center + }; + + content.Children.Add(new System.Windows.Controls.TextBlock + { + Text = $"📦 收到 {newOrderCount} 个新订单!", + FontSize = 16, + FontWeight = FontWeights.Bold, + Margin = new Thickness(0, 0, 0, 10) + }); + + content.Children.Add(new System.Windows.Controls.TextBlock + { + Text = $"待发货订单:{PendingCount} 个", + FontSize = 13, + Foreground = System.Windows.Media.Brushes.Gray + }); + + notification.Content = content; + + // 5秒后自动关闭 + var closeTimer = new DispatcherTimer + { + Interval = TimeSpan.FromSeconds(5) + }; + closeTimer.Tick += (s, e) => + { + closeTimer.Stop(); + notification.Close(); + }; + + notification.MouseLeftButtonDown += (s, e) => + { + notification.Close(); + mainWindow.Activate(); + if (mainWindow.WindowState == WindowState.Minimized) + mainWindow.WindowState = WindowState.Normal; + }; + + notification.Show(); + closeTimer.Start(); + } + else + { + // 窗口在前台时,只显示状态栏消息 + StatusMessage = $"📦 收到 {newOrderCount} 个新订单!待发货 {PendingCount} 个"; + } + } + }); + } + + private void FlashWindow(Window window) + { + // 简单的窗口闪烁效果 + try + { + var hwnd = new System.Windows.Interop.WindowInteropHelper(window).Handle; + if (hwnd != IntPtr.Zero) + { + var info = new FLASHWINFO + { + cbSize = Convert.ToUInt32(System.Runtime.InteropServices.Marshal.SizeOf(typeof(FLASHWINFO))), + hwnd = hwnd, + dwFlags = 3, // FLASHW_ALL + uCount = 3, + dwTimeout = 0 + }; + FlashWindowEx(ref info); + } + } + catch + { + // 忽略闪烁失败 + } + } + + [System.Runtime.InteropServices.DllImport("user32.dll")] + private static extern bool FlashWindowEx(ref FLASHWINFO pwfi); + + [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)] + private struct FLASHWINFO + { + public uint cbSize; + public IntPtr hwnd; + public uint dwFlags; + public uint uCount; + public uint dwTimeout; + } + + partial void OnAutoSyncEnabledChanged(bool value) + { + if (value) + { + StartAutoSync(); + } + else + { + StopAutoSync(); + } } [RelayCommand] @@ -90,7 +349,7 @@ namespace PackagingMallShipper.ViewModels }; var orders = await _orderService.GetOrdersAsync(status, SearchText); - + Orders.Clear(); foreach (var order in orders) { @@ -119,9 +378,19 @@ namespace PackagingMallShipper.ViewModels try { var result = await _syncService.SyncOrdersAsync(SyncMode.Incremental); + LastSyncTime = DateTime.Now; await RefreshOrdersAsync(); await UpdateCountsAsync(); - StatusMessage = $"同步完成!新增 {result.NewCount},更新 {result.UpdatedCount}"; + + if (result.NewCount > 0) + { + StatusMessage = $"同步完成!新增 {result.NewCount},更新 {result.UpdatedCount}"; + ShowNewOrderNotification(result.NewCount); + } + else + { + StatusMessage = $"同步完成!新增 {result.NewCount},更新 {result.UpdatedCount}"; + } } catch (UnauthorizedAccessException) { @@ -137,6 +406,7 @@ namespace PackagingMallShipper.ViewModels { IsBusy = false; SyncProgress = ""; + UpdateNextSyncTimeDisplay(); } } @@ -154,6 +424,7 @@ namespace PackagingMallShipper.ViewModels try { var syncResult = await _syncService.SyncOrdersAsync(SyncMode.Full); + LastSyncTime = DateTime.Now; await RefreshOrdersAsync(); await UpdateCountsAsync(); StatusMessage = $"全量同步完成!共 {syncResult.TotalCount} 条"; @@ -165,6 +436,7 @@ namespace PackagingMallShipper.ViewModels finally { IsBusy = false; + UpdateNextSyncTimeDisplay(); } } @@ -284,5 +556,10 @@ namespace PackagingMallShipper.ViewModels { // Debounce search - simple implementation } + + public void Cleanup() + { + _autoSyncTimer?.Stop(); + } } } diff --git a/PackagingMallShipper/Views/OrderListView.xaml b/PackagingMallShipper/Views/OrderListView.xaml index 9e87f55..1e5ffc9 100644 --- a/PackagingMallShipper/Views/OrderListView.xaml +++ b/PackagingMallShipper/Views/OrderListView.xaml @@ -15,16 +15,39 @@ - - - - + + + + + + + + + + - - - + + + + + + + + + + + + + - +