������� �� Avalonia 2.0 (�����-���������, ����������, �������� ��������) #1
@ -116,6 +116,25 @@
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
</Style>
|
||||
|
||||
<!-- ============ Верхняя навигация (как в WinForms-версии) ============ -->
|
||||
<Style Selector="ListBox.topnav">
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="BorderThickness" Value="0"/>
|
||||
<Setter Property="Padding" Value="0"/>
|
||||
</Style>
|
||||
<Style Selector="ListBox.topnav ListBoxItem">
|
||||
<Setter Property="Padding" Value="10,7"/>
|
||||
<Setter Property="Margin" Value="3,0"/>
|
||||
<Setter Property="CornerRadius" Value="10"/>
|
||||
<Setter Property="Cursor" Value="Hand"/>
|
||||
</Style>
|
||||
<Style Selector="ListBox.topnav ListBoxItem:pointerover /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource SidebarActive}"/>
|
||||
</Style>
|
||||
<Style Selector="ListBox.topnav ListBoxItem:selected /template/ ContentPresenter">
|
||||
<Setter Property="Background" Value="{DynamicResource SidebarActive}"/>
|
||||
</Style>
|
||||
|
||||
<!-- ============ Таблица ============ -->
|
||||
<Style Selector="DataGrid">
|
||||
<Setter Property="Background" Value="{DynamicResource Surface}"/>
|
||||
@ -139,6 +158,10 @@
|
||||
<Style Selector="DataGridCell">
|
||||
<Setter Property="Padding" Value="10,0"/>
|
||||
</Style>
|
||||
<!-- числовые ячейки — вправо -->
|
||||
<Style Selector="DataGridCell.num TextBlock">
|
||||
<Setter Property="TextAlignment" Value="Right"/>
|
||||
</Style>
|
||||
<Style Selector="DataGridRow:selected /template/ Rectangle#BackgroundRectangle">
|
||||
<Setter Property="Fill" Value="{DynamicResource AccentSoft}"/>
|
||||
<Setter Property="Opacity" Value="1"/>
|
||||
|
||||
99
src/Elfisa.Avalonia/ViewModels/OrdersViewModel.cs
Normal file
99
src/Elfisa.Avalonia/ViewModels/OrdersViewModel.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Elfisa.Core;
|
||||
|
||||
namespace Elfisa.Avalonia.ViewModels;
|
||||
|
||||
public sealed class OrderItemRow
|
||||
{
|
||||
public string Name { get; init; } = "";
|
||||
public string Code { get; init; } = "";
|
||||
public decimal Qty { get; init; }
|
||||
public decimal Price { get; init; }
|
||||
public decimal Sum { get; init; }
|
||||
}
|
||||
|
||||
public sealed class OrderSummary
|
||||
{
|
||||
public string Date { get; init; } = "";
|
||||
public string Number { get; init; } = "";
|
||||
public string Supplier { get; init; } = "";
|
||||
public string Location { get; init; } = "";
|
||||
public string Status { get; init; } = "";
|
||||
public int ItemsCount { get; init; }
|
||||
public decimal Sum { get; init; }
|
||||
public ObservableCollection<OrderItemRow> Items { get; init; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Заказы: список + состав. Пока строится из /api/buyer/report (он работает);
|
||||
/// после деплоя фикса /api/buyer/orders можно перейти на «родной» эндпоинт
|
||||
/// (даст ещё PlacedAt, Comment, отмену/размещение).
|
||||
/// </summary>
|
||||
public partial class OrdersViewModel : ViewModelBase
|
||||
{
|
||||
private readonly ApiClient _api = new();
|
||||
|
||||
public ObservableCollection<OrderSummary> Orders { get; } = new();
|
||||
|
||||
[ObservableProperty] private OrderSummary? _selectedOrder;
|
||||
[ObservableProperty] private bool _busy;
|
||||
[ObservableProperty] private string? _status;
|
||||
|
||||
public OrdersViewModel()
|
||||
{
|
||||
_api.SetToken(Session.Current.Token);
|
||||
if (Session.Current.IsAuthenticated) _ = LoadAsync();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
Status = null;
|
||||
try
|
||||
{
|
||||
Busy = true;
|
||||
var to = DateTime.Today;
|
||||
var from = to.AddMonths(-24);
|
||||
var lines = await _api.GetBuyerReportAsync(from, to);
|
||||
|
||||
Orders.Clear();
|
||||
foreach (var g in lines.GroupBy(l => l.OrderId)
|
||||
.OrderByDescending(x => x.First().OrderDate ?? DateTime.MinValue))
|
||||
{
|
||||
var f = g.First();
|
||||
var items = new ObservableCollection<OrderItemRow>(
|
||||
g.Select(l => new OrderItemRow
|
||||
{
|
||||
Name = l.ItemName ?? "",
|
||||
Code = l.ItemCode ?? "",
|
||||
Qty = l.Qty,
|
||||
Price = l.UnitPrice,
|
||||
Sum = l.Sum
|
||||
}));
|
||||
|
||||
Orders.Add(new OrderSummary
|
||||
{
|
||||
Date = f.OrderDate?.ToLocalTime().ToString("dd.MM.yyyy") ?? "",
|
||||
Number = f.GlobalSign ?? "",
|
||||
Supplier = string.Join(", ", g.Select(x => x.Supplier)
|
||||
.Where(s => !string.IsNullOrWhiteSpace(s)).Distinct()),
|
||||
Location = f.Location ?? "",
|
||||
Status = f.Status ?? "",
|
||||
ItemsCount = g.Count(),
|
||||
Sum = g.Sum(x => x.Sum),
|
||||
Items = items
|
||||
});
|
||||
}
|
||||
|
||||
SelectedOrder = Orders.FirstOrDefault();
|
||||
if (Orders.Count == 0) Status = "Заказов за последние 2 года нет.";
|
||||
}
|
||||
catch (Exception ex) { Status = ex.Message; }
|
||||
finally { Busy = false; }
|
||||
}
|
||||
}
|
||||
@ -42,13 +42,13 @@ public partial class ShellViewModel : ViewModelBase
|
||||
_logout = logout;
|
||||
NavItems = new ObservableCollection<NavItem>
|
||||
{
|
||||
new("Прайс-лист", "P", () => new PlaceholderPageViewModel("Прайс-лист", "Каталог, поиск и добавление позиций в заказ.")),
|
||||
new("Заказы", "З", () => new PlaceholderPageViewModel("Заказы", "История заказов и их статусы.")),
|
||||
new("Накладные", "Н", () => new PlaceholderPageViewModel("Накладные", "Приход товара по заказам.")),
|
||||
new("Отчёты", "О", () => new ReportsViewModel()),
|
||||
new("Отправить", "→", () => new PlaceholderPageViewModel("Отправить", "Формирование и выгрузка заявок поставщику (DBF).")),
|
||||
new("Отказы", "!", () => new PlaceholderPageViewModel("Отказы", "Отказные позиции по заказам.")),
|
||||
new("Справочник", "С", () => new PlaceholderPageViewModel("Справочник", "Контрагенты, грузополучатели, настройки.")),
|
||||
new("Прайс-лист", "🧾", () => new PlaceholderPageViewModel("Прайс-лист", "Каталог, поиск и добавление позиций в заказ.")),
|
||||
new("Заказы", "🛒", () => new OrdersViewModel()),
|
||||
new("Накладные", "📄", () => new PlaceholderPageViewModel("Накладные", "Приход товара по заказам.")),
|
||||
new("Отчёты", "📊", () => new ReportsViewModel()),
|
||||
new("Отправить", "📤", () => new PlaceholderPageViewModel("Отправить", "Формирование и выгрузка заявок поставщику (DBF).")),
|
||||
new("Отказы", "⛔", () => new PlaceholderPageViewModel("Отказы", "Отказные позиции по заказам.")),
|
||||
new("Справочник", "📖", () => new PlaceholderPageViewModel("Справочник", "Контрагенты, грузополучатели, настройки.")),
|
||||
};
|
||||
SelectedNav = NavItems.First(n => n.Title == "Отчёты");
|
||||
}
|
||||
|
||||
75
src/Elfisa.Avalonia/Views/OrdersView.axaml
Normal file
75
src/Elfisa.Avalonia/Views/OrdersView.axaml
Normal file
@ -0,0 +1,75 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:vm="using:Elfisa.Avalonia.ViewModels"
|
||||
xmlns:conv="using:Avalonia.Data.Converters"
|
||||
x:Class="Elfisa.Avalonia.Views.OrdersView"
|
||||
x:DataType="vm:OrdersViewModel">
|
||||
|
||||
<Grid Margin="24" RowDefinitions="Auto,*">
|
||||
<StackPanel Grid.Row="0" Spacing="2" Margin="0,0,0,14">
|
||||
<TextBlock Text="Заказы" Classes="h1"/>
|
||||
<TextBlock Text="История заказов и их состав" Classes="muted"/>
|
||||
</StackPanel>
|
||||
|
||||
<Grid Grid.Row="1" ColumnDefinitions="*,16,430">
|
||||
|
||||
<!-- Список заказов -->
|
||||
<Border Grid.Column="0" Classes="card" Padding="0" ClipToBounds="True">
|
||||
<DataGrid ItemsSource="{Binding Orders}" SelectedItem="{Binding SelectedOrder}"
|
||||
x:CompileBindings="False"
|
||||
AutoGenerateColumns="False" IsReadOnly="True" HeadersVisibility="Column"
|
||||
CanUserResizeColumns="True" CanUserSortColumns="False">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Дата" Width="112" Binding="{Binding Date}"/>
|
||||
<DataGridTextColumn Header="Номер" Width="140" Binding="{Binding Number}"/>
|
||||
<DataGridTextColumn Header="Поставщик" Width="*" Binding="{Binding Supplier}"/>
|
||||
<DataGridTextColumn Header="Позиций" Width="90" Binding="{Binding ItemsCount}" CellStyleClasses="num"/>
|
||||
<DataGridTextColumn Header="Сумма" Width="132" Binding="{Binding Sum, StringFormat='{}{0:N2}'}" CellStyleClasses="num"/>
|
||||
<DataGridTextColumn Header="Статус" Width="95" Binding="{Binding Status}"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</Border>
|
||||
|
||||
<!-- Карточка заказа -->
|
||||
<Border Grid.Column="2" Classes="card" Padding="18">
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Top" Spacing="4" Margin="0,0,0,14">
|
||||
<TextBlock Text="{Binding SelectedOrder.Number}" Classes="h2"/>
|
||||
<StackPanel Orientation="Horizontal" Spacing="10">
|
||||
<TextBlock Text="{Binding SelectedOrder.Date}" Classes="muted"/>
|
||||
<TextBlock Text="{Binding SelectedOrder.Status}" Classes="muted"/>
|
||||
</StackPanel>
|
||||
<TextBlock Text="{Binding SelectedOrder.Supplier}" Classes="muted" TextWrapping="Wrap"/>
|
||||
<TextBlock Text="{Binding SelectedOrder.Location}" Classes="muted" TextWrapping="Wrap"/>
|
||||
</StackPanel>
|
||||
|
||||
<Border DockPanel.Dock="Bottom" Margin="0,12,0,0" Padding="0,10,0,0"
|
||||
BorderThickness="0,1,0,0" BorderBrush="{DynamicResource Border}">
|
||||
<Grid ColumnDefinitions="*,Auto">
|
||||
<TextBlock Grid.Column="0" Text="Итого" FontWeight="SemiBold" Foreground="{DynamicResource Text}"/>
|
||||
<TextBlock Grid.Column="1" Text="{Binding SelectedOrder.Sum, StringFormat='{}{0:N2}'}"
|
||||
FontWeight="SemiBold" Foreground="{DynamicResource Accent}"/>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<DataGrid ItemsSource="{Binding SelectedOrder.Items}" x:CompileBindings="False"
|
||||
AutoGenerateColumns="False" IsReadOnly="True" HeadersVisibility="Column"
|
||||
CanUserResizeColumns="True" CanUserSortColumns="False">
|
||||
<DataGrid.Columns>
|
||||
<DataGridTextColumn Header="Товар" Width="*" Binding="{Binding Name}"/>
|
||||
<DataGridTextColumn Header="Кол-во" Width="78" Binding="{Binding Qty, StringFormat='{}{0:N0}'}" CellStyleClasses="num"/>
|
||||
<DataGridTextColumn Header="Сумма" Width="110" Binding="{Binding Sum, StringFormat='{}{0:N2}'}" CellStyleClasses="num"/>
|
||||
</DataGrid.Columns>
|
||||
</DataGrid>
|
||||
</DockPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
<!-- Статус -->
|
||||
<Border Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Left" Margin="4"
|
||||
Padding="10,7" CornerRadius="6" Background="{DynamicResource AccentSoft}"
|
||||
IsVisible="{Binding Status, Converter={x:Static conv:ObjectConverters.IsNotNull}}">
|
||||
<TextBlock Text="{Binding Status}" Foreground="{DynamicResource AccentPressed}" FontSize="12.5"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
9
src/Elfisa.Avalonia/Views/OrdersView.axaml.cs
Normal file
9
src/Elfisa.Avalonia/Views/OrdersView.axaml.cs
Normal file
@ -0,0 +1,9 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Elfisa.Avalonia.Views;
|
||||
|
||||
public partial class OrdersView : UserControl
|
||||
{
|
||||
public OrdersView() => AvaloniaXamlLoader.Load(this);
|
||||
}
|
||||
@ -4,47 +4,54 @@
|
||||
x:Class="Elfisa.Avalonia.Views.ShellView"
|
||||
x:DataType="vm:ShellViewModel">
|
||||
|
||||
<Grid ColumnDefinitions="250,*">
|
||||
|
||||
<!-- Сайдбар -->
|
||||
<Border Grid.Column="0" Background="{DynamicResource Sidebar}">
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Top" Margin="22,24,20,18" Spacing="2">
|
||||
<TextBlock Text="ЭльФиСА" FontSize="21" FontWeight="Bold" Foreground="White"/>
|
||||
<TextBlock Text="Электронная Фармация" Foreground="{DynamicResource SidebarText}" FontSize="11"/>
|
||||
|
||||
<!-- Верхняя панель навигации (как в исходной программе) -->
|
||||
<Border DockPanel.Dock="Top" Background="{DynamicResource Sidebar}" Height="82">
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" Margin="18,0">
|
||||
|
||||
<!-- Логотип -->
|
||||
<StackPanel Grid.Column="0" VerticalAlignment="Center" Margin="0,0,22,0">
|
||||
<TextBlock Text="ЭльФиСА" FontSize="19" FontWeight="Bold" Foreground="White"/>
|
||||
<TextBlock Text="Электронная Фармация" Foreground="{DynamicResource SidebarText}" FontSize="10.5"/>
|
||||
</StackPanel>
|
||||
|
||||
<Border DockPanel.Dock="Bottom" Margin="14,10,14,16" Padding="14" CornerRadius="12"
|
||||
Background="{DynamicResource SidebarActive}">
|
||||
<StackPanel Spacing="9">
|
||||
<TextBlock Text="{Binding UserName}" Foreground="White" FontWeight="SemiBold"
|
||||
FontSize="12.5" TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock Text="{Binding RoleLabel}" Foreground="{DynamicResource SidebarText}" FontSize="11"/>
|
||||
<Button Content="Выйти" Command="{Binding LogoutCommand}" HorizontalAlignment="Stretch"
|
||||
Background="Transparent" Foreground="White" BorderBrush="#55FFFFFF"
|
||||
BorderThickness="1" CornerRadius="8" Padding="0,7" Cursor="Hand"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<ListBox Classes="nav" Margin="0,6,0,0"
|
||||
<!-- Навигация -->
|
||||
<ListBox Grid.Column="1" Classes="topnav" VerticalAlignment="Center" HorizontalAlignment="Left"
|
||||
ItemsSource="{Binding NavItems}" SelectedItem="{Binding SelectedNav}">
|
||||
<ListBox.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Orientation="Horizontal"/>
|
||||
</ItemsPanelTemplate>
|
||||
</ListBox.ItemsPanel>
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate x:DataType="vm:NavItem">
|
||||
<StackPanel Orientation="Horizontal" Spacing="12">
|
||||
<Border Width="26" Height="26" CornerRadius="7" Background="#26FFFFFF">
|
||||
<TextBlock Text="{Binding Icon}" Foreground="White" FontSize="12.5"
|
||||
FontWeight="SemiBold"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<TextBlock Text="{Binding Title}" VerticalAlignment="Center"/>
|
||||
<StackPanel Orientation="Vertical" Width="76" Spacing="3">
|
||||
<TextBlock Text="{Binding Icon}" FontSize="17" HorizontalAlignment="Center"/>
|
||||
<TextBlock Text="{Binding Title}" Foreground="{DynamicResource SidebarText}"
|
||||
FontSize="11.5" HorizontalAlignment="Center"
|
||||
TextTrimming="CharacterEllipsis"/>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</ListBox.ItemTemplate>
|
||||
</ListBox>
|
||||
</DockPanel>
|
||||
|
||||
<!-- Пользователь + выход -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="14" VerticalAlignment="Center" Margin="18,0,0,0">
|
||||
<StackPanel VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding UserName}" Foreground="White" FontSize="12" FontWeight="SemiBold"
|
||||
HorizontalAlignment="Right" TextTrimming="CharacterEllipsis"/>
|
||||
<TextBlock Text="{Binding RoleLabel}" Foreground="{DynamicResource SidebarText}"
|
||||
FontSize="10.5" HorizontalAlignment="Right"/>
|
||||
</StackPanel>
|
||||
<Button Content="Выйти" Command="{Binding LogoutCommand}"
|
||||
Background="Transparent" Foreground="White" BorderBrush="#55FFFFFF"
|
||||
BorderThickness="1" CornerRadius="8" Padding="16,9" Cursor="Hand"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
<!-- Контент -->
|
||||
<ContentControl Grid.Column="1" Content="{Binding CurrentPage}"/>
|
||||
</Grid>
|
||||
<ContentControl Content="{Binding CurrentPage}"/>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user