From 43468ac693474f5e95ce50d2b59404b57598dd46 Mon Sep 17 00:00:00 2001 From: Exest Date: Sat, 8 Aug 2026 11:05:52 +0300 Subject: [PATCH] =?UTF-8?q?Avalonia:=20=D1=80=D0=B0=D0=B7=D0=B4=D0=B5?= =?UTF-8?q?=D0=BB=20=C2=AB=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=C2=BB=20(=D1=81=D0=BA=D0=B0=D1=87=D0=B8=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B9=D1=81=D0=B0)=20+?= =?UTF-8?q?=20=D1=83=D0=BF=D0=BB=D0=BE=D1=82=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=20=D0=B2=D0=B5=D1=80=D1=85=D0=BD=D0=B5=D0=B9=20=D0=BF=D0=B0?= =?UTF-8?q?=D0=BD=D0=B5=D0=BB=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлена вкладка «Загрузить» (между Отчёты и Отправить, как в исходной программе): постраничная загрузка прайса с прогрессом, кэш в памяти (PriceCache), итог «N позиций от K поставщиков» + timestamp - Верхняя навигация уплотнена под 8 вкладок (Справочник больше не обрезается) - Core: PriceCache Проверено вживую: все 8 вкладок влезают, загрузка прайса работает (500 поз.). Co-Authored-By: Claude Opus 4.8 --- src/Elfisa.Avalonia/Styles/Elfisa.axaml | 4 +- .../ViewModels/LoadPriceViewModel.cs | 78 +++++++++++++++++++ .../ViewModels/ShellViewModel.cs | 1 + src/Elfisa.Avalonia/Views/LoadPriceView.axaml | 39 ++++++++++ .../Views/LoadPriceView.axaml.cs | 9 +++ src/Elfisa.Avalonia/Views/ShellView.axaml | 14 ++-- src/Elfisa.Core/PriceCache.cs | 9 +++ 7 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 src/Elfisa.Avalonia/ViewModels/LoadPriceViewModel.cs create mode 100644 src/Elfisa.Avalonia/Views/LoadPriceView.axaml create mode 100644 src/Elfisa.Avalonia/Views/LoadPriceView.axaml.cs create mode 100644 src/Elfisa.Core/PriceCache.cs diff --git a/src/Elfisa.Avalonia/Styles/Elfisa.axaml b/src/Elfisa.Avalonia/Styles/Elfisa.axaml index a56e3e1..70529ab 100644 --- a/src/Elfisa.Avalonia/Styles/Elfisa.axaml +++ b/src/Elfisa.Avalonia/Styles/Elfisa.axaml @@ -123,8 +123,8 @@ diff --git a/src/Elfisa.Avalonia/ViewModels/LoadPriceViewModel.cs b/src/Elfisa.Avalonia/ViewModels/LoadPriceViewModel.cs new file mode 100644 index 0000000..b8fd1e5 --- /dev/null +++ b/src/Elfisa.Avalonia/ViewModels/LoadPriceViewModel.cs @@ -0,0 +1,78 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Elfisa.Core; + +namespace Elfisa.Avalonia.ViewModels; + +/// «Загрузить» — скачивание актуального прайса с сервера в память. +public partial class LoadPriceViewModel : ViewModelBase +{ + private readonly ApiClient _api = new(); + + [ObservableProperty] private bool _busy; + [ObservableProperty] private double _progress; // 0..100 + [ObservableProperty] private string _progressText = ""; + [ObservableProperty] private string? _result; + + public string LastLoaded => + PriceCache.LoadedAt is { } dt + ? $"Последняя загрузка: {dt:dd.MM.yyyy HH:mm} — {PriceCache.Items.Count} позиций" + : "Прайс ещё не загружался в этой сессии."; + + public LoadPriceViewModel() + { + _api.SetToken(Session.Current.Token); + } + + [RelayCommand] + private async Task LoadAsync() + { + Result = null; + try + { + Busy = true; + Progress = 0; + ProgressText = "Соединение с сервером…"; + + var all = new List(); + const int page = 500; + int offset = 0, total = 0; + + while (true) + { + var resp = await _api.GetPriceSummaryAsync(null, limit: page, offset: offset); + var batch = resp.Summary ?? new(); + if (resp.TotalDrugs > 0) total = resp.TotalDrugs; + all.AddRange(batch); + + Progress = total > 0 ? Math.Min(100.0, all.Count * 100.0 / total) : 0; + ProgressText = total > 0 + ? $"Загружено {all.Count} из {total} позиций…" + : $"Загружено {all.Count} позиций…"; + + offset += batch.Count; + if (batch.Count < page || (total > 0 && all.Count >= total) || offset > 30000) + break; + } + + PriceCache.Items = all; + PriceCache.LoadedAt = DateTime.Now; + Progress = 100; + ProgressText = "Готово."; + + var suppliers = all.Select(x => x.SupplierName).Where(s => !string.IsNullOrWhiteSpace(s)).Distinct().Count(); + Result = $"Прайс загружен: {all.Count} позиций от {suppliers} поставщик(ов)."; + OnPropertyChanged(nameof(LastLoaded)); + } + catch (Exception ex) + { + Result = "Ошибка загрузки: " + ex.Message; + ProgressText = ""; + } + finally { Busy = false; } + } +} diff --git a/src/Elfisa.Avalonia/ViewModels/ShellViewModel.cs b/src/Elfisa.Avalonia/ViewModels/ShellViewModel.cs index 24b8851..060acba 100644 --- a/src/Elfisa.Avalonia/ViewModels/ShellViewModel.cs +++ b/src/Elfisa.Avalonia/ViewModels/ShellViewModel.cs @@ -46,6 +46,7 @@ public partial class ShellViewModel : ViewModelBase new("Заказы", "🛒", () => new OrdersViewModel()), new("Накладные", "📄", () => new InvoicesViewModel()), new("Отчёты", "📊", () => new ReportsViewModel()), + new("Загрузить", "📥", () => new LoadPriceViewModel()), new("Отправить", "📤", () => new SendOrderViewModel()), new("Отказы", "⛔", () => new PlaceholderPageViewModel("Отказы", "Отказные позиции по заказам. Нужен серверный эндпоинт: данные (RefuseItemsCount) сейчас есть только в локальной БД WinForms-клиента.")), diff --git a/src/Elfisa.Avalonia/Views/LoadPriceView.axaml b/src/Elfisa.Avalonia/Views/LoadPriceView.axaml new file mode 100644 index 0000000..77737a0 --- /dev/null +++ b/src/Elfisa.Avalonia/Views/LoadPriceView.axaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + +