Not a member of GistPad yet?
Sign Up,
it unlocks many cool features!
- ================================================================================
- ПРОЕКТ: AvaloniaApplication1 (Магазин обуви Botinki)
- ================================================================================
- --------------------------------------------------------------------------------
- ФАЙЛ: AvaloniaApplication1.csproj
- --------------------------------------------------------------------------------
- <Project Sdk="Microsoft.NET.Sdk">
- <PropertyGroup>
- <OutputType>WinExe</OutputType>
- <TargetFramework>net10.0</TargetFramework>
- <Nullable>enable</Nullable>
- <ApplicationManifest>app.manifest</ApplicationManifest>
- <RootNamespace>AvaloniaApplication1</RootNamespace>
- </PropertyGroup>
- <ItemGroup>
- <PackageReference Include="Avalonia" Version="11.3.11" />
- <PackageReference Include="Avalonia.Desktop" Version="11.3.11" />
- <PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.11" />
- <PackageReference Include="Npgsql" Version="8.0.5" />
- </ItemGroup>
- <ItemGroup>
- <AvaloniaResource Include="Assets\**\*" />
- <None Include="Assets\**\*">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- </ItemGroup>
- </Project>
- --------------------------------------------------------------------------------
- ФАЙЛ: Program.cs
- --------------------------------------------------------------------------------
- using System;
- using Avalonia;
- namespace AvaloniaApplication1;
- internal static class Program
- {
- [STAThread]
- public static void Main(string[] args) => BuildAvaloniaApp()
- .StartWithClassicDesktopLifetime(args);
- public static AppBuilder BuildAvaloniaApp()
- => AppBuilder.Configure<App>()
- .UsePlatformDetect()
- .LogToTrace();
- }
- --------------------------------------------------------------------------------
- ФАЙЛ: App.axaml
- --------------------------------------------------------------------------------
- <Application xmlns="https://github.com/avaloniaui"
- x:Class="AvaloniaApplication1.App"
- RequestedThemeVariant="Default">
- <Application.Resources>
- <Color x:Key="PrimaryBackgroundColor">#FFFFFF</Color>
- <Color x:Key="SecondaryBackgroundColor">#7FFF00</Color>
- <Color x:Key="AccentColor">#00FA9A</Color>
- <SolidColorBrush x:Key="PrimaryBackgroundBrush" Color="{DynamicResource PrimaryBackgroundColor}" />
- <SolidColorBrush x:Key="SecondaryBackgroundBrush" Color="{DynamicResource SecondaryBackgroundColor}" />
- <SolidColorBrush x:Key="AccentBrush" Color="{DynamicResource AccentColor}" />
- </Application.Resources>
- <Application.Styles>
- <FluentTheme />
- <Style Selector="Window">
- <Setter Property="Background" Value="{DynamicResource PrimaryBackgroundBrush}" />
- </Style>
- <Style Selector="TextBox">
- <Setter Property="Background" Value="{DynamicResource PrimaryBackgroundBrush}" />
- <Setter Property="BorderBrush" Value="{DynamicResource AccentBrush}" />
- </Style>
- <Style Selector="Button">
- <Setter Property="Background" Value="{DynamicResource SecondaryBackgroundBrush}" />
- <Setter Property="BorderBrush" Value="{DynamicResource AccentBrush}" />
- <Setter Property="Foreground" Value="Black" />
- </Style>
- <Style Selector="Border.card">
- <Setter Property="Background" Value="{DynamicResource PrimaryBackgroundBrush}" />
- <Setter Property="BorderBrush" Value="{DynamicResource AccentBrush}" />
- </Style>
- </Application.Styles>
- </Application>
- --------------------------------------------------------------------------------
- ФАЙЛ: App.axaml.cs
- --------------------------------------------------------------------------------
- using Avalonia;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Markup.Xaml;
- using AvaloniaApplication1.Views;
- namespace AvaloniaApplication1;
- public partial class App : Application
- {
- public override void Initialize()
- {
- AvaloniaXamlLoader.Load(this);
- }
- public override void OnFrameworkInitializationCompleted()
- {
- if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- desktop.MainWindow = new LoginWindow();
- base.OnFrameworkInitializationCompleted();
- }
- }
- --------------------------------------------------------------------------------
- ФАЙЛ: Models/User.cs
- --------------------------------------------------------------------------------
- namespace AvaloniaApplication1.Models;
- public sealed class User
- {
- public int Id { get; set; }
- public string Login { get; set; } = "";
- public string Password { get; set; } = "";
- public string Role { get; set; } = "";
- public string FullName { get; set; } = "";
- }
- --------------------------------------------------------------------------------
- ФАЙЛ: Models/Product.cs
- --------------------------------------------------------------------------------
- using System;
- using System.IO;
- namespace AvaloniaApplication1.Models;
- public sealed class Product
- {
- private static readonly string DefaultPhotoPath;
- static Product()
- {
- // Find default photo in Assets
- var baseDir = AppContext.BaseDirectory;
- var candidates = new[]
- {
- Path.Combine(baseDir, "Assets", "Icon.png"),
- Path.Combine(baseDir, "Assets", "Icon.JPG"),
- Path.Combine(baseDir, "Assets", "Icon.ico"),
- Path.Combine(baseDir, "..", "..", "..", "Assets", "Icon.png"),
- Path.Combine(baseDir, "..", "..", "..", "Assets", "Icon.JPG"),
- Path.Combine(Environment.CurrentDirectory, "Assets", "Icon.png"),
- Path.Combine(Environment.CurrentDirectory, "Assets", "Icon.JPG"),
- };
- DefaultPhotoPath = string.Empty;
- foreach (var candidate in candidates)
- {
- var fullPath = Path.GetFullPath(candidate);
- if (File.Exists(fullPath))
- {
- DefaultPhotoPath = fullPath;
- break;
- }
- }
- }
- public int ProductId { get; set; }
- public string Article { get; set; } = "";
- public int CategoryId { get; set; }
- public int SupplierId { get; set; }
- public int TypeProductId { get; set; }
- public decimal Price { get; set; }
- public string Discount { get; set; } = "0";
- public string Description { get; set; } = "";
- private string _photo = "";
- public string Photo
- {
- get => string.IsNullOrWhiteSpace(_photo) ? DefaultPhotoPath : _photo;
- set => _photo = value ?? "";
- }
- public decimal FinalPrice
- {
- get
- {
- if (!decimal.TryParse(Discount.Replace("%", ""), out var discountValue))
- {
- discountValue = 0;
- }
- return Price * (100m - discountValue) / 100m;
- }
- }
- }
- --------------------------------------------------------------------------------
- ФАЙЛ: Models/Order.cs
- --------------------------------------------------------------------------------
- namespace AvaloniaApplication1.Models;
- public sealed class Order
- {
- public int OrderId { get; set; }
- public string OrderArticle { get; set; } = "";
- public string DateOrder { get; set; } = "";
- public string DateDelivery { get; set; } = "";
- public int PickUpPointId { get; set; }
- public string Code { get; set; } = "";
- public string StatusOrder { get; set; } = "Новый";
- public int UserId { get; set; }
- public decimal TotalSum { get; set; }
- }
- --------------------------------------------------------------------------------
- ФАЙЛ: services/DbService.cs
- --------------------------------------------------------------------------------
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Linq;
- using AvaloniaApplication1.Models;
- using Npgsql;
- namespace AvaloniaApplication1.Services;
- public sealed class DbService
- {
- // Меняешь тут: свою БД, логин, пароль
- private readonly string _connectionString = "Host=localhost;Port=5432;Username=postgres;Password=123;Database=Botinki";
- // ========== АВТОРИЗАЦИЯ ==========
- public User? Login(string login, string password)
- {
- using var conn = OpenConnection();
- using var cmd = new NpgsqlCommand(
- """
- SELECT u.user_id, u.login, u.password, COALESCE(r.name_role, 'Клиент') AS role_name,
- TRIM(COALESCE(u.name, '') || ' ' || COALESCE(u.second_name, '') || ' ' || COALESCE(u.patronymic, '')) AS full_name
- FROM users u
- LEFT JOIN roles r ON r.role_id = u.role_id
- WHERE u.login = @l AND u.password = @p
- """, conn);
- cmd.Parameters.AddWithValue("l", login);
- cmd.Parameters.AddWithValue("p", password);
- using var reader = cmd.ExecuteReader();
- if (reader.Read())
- {
- return new User
- {
- Id = reader.GetInt32(0),
- Login = reader.GetString(1),
- Password = reader.GetString(2),
- Role = NormalizeRole(reader.GetString(3)),
- FullName = reader.GetString(4)
- };
- }
- return null;
- }
- // ========== ТОВАРЫ ==========
- public List<Product> GetProducts()
- {
- var list = new List<Product>();
- using var conn = OpenConnection();
- using var cmd = new NpgsqlCommand(
- """
- SELECT product_id, article, category_id, supplier_id, price, discount, description, photo
- FROM products
- ORDER BY product_id
- """, conn);
- using var reader = cmd.ExecuteReader();
- while (reader.Read())
- {
- list.Add(new Product
- {
- ProductId = reader.GetInt32(0),
- Article = reader.GetString(1),
- CategoryId = reader.IsDBNull(2) ? 0 : reader.GetInt32(2),
- SupplierId = reader.IsDBNull(3) ? 0 : reader.GetInt32(3),
- TypeProductId = 0,
- Price = reader.IsDBNull(4) ? 0m : reader.GetDecimal(4),
- Discount = reader.IsDBNull(5) ? "0" : reader.GetString(5),
- Description = reader.IsDBNull(6) ? "" : reader.GetString(6),
- Photo = reader.IsDBNull(7) ? "" : ResolvePhotoPath(reader.GetString(7))
- });
- }
- return list;
- }
- // ========== ПОИСК ФОТО ==========
- private static string ResolvePhotoPath(string photoValue)
- {
- if (string.IsNullOrWhiteSpace(photoValue)) return "";
- var value = photoValue.Trim();
- if (Path.IsPathRooted(value) && File.Exists(value))
- return value;
- var fileName = Path.GetFileNameWithoutExtension(value);
- var directories = new[]
- {
- Path.Combine(AppContext.BaseDirectory, "Assets"),
- Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "Assets")),
- Path.Combine(Environment.CurrentDirectory, "Assets")
- };
- var extensions = new[] { ".jpg", ".jpeg", ".png", ".gif", ".bmp" };
- foreach (var dir in directories)
- {
- var exact = Path.Combine(dir, value);
- if (File.Exists(exact)) return exact;
- foreach (var ext in extensions)
- {
- var candidate = Path.Combine(dir, fileName + ext);
- if (File.Exists(candidate)) return candidate;
- }
- }
- return "";
- }
- // ========== ПОДКЛЮЧЕНИЕ ==========
- private NpgsqlConnection OpenConnection()
- {
- var conn = new NpgsqlConnection(_connectionString);
- conn.Open();
- return conn;
- }
- // ========== НОРМАЛИЗАЦИЯ РОЛИ ==========
- private static string NormalizeRole(string role)
- {
- var r = role.Trim().ToLowerInvariant();
- return r switch
- {
- "администратор" => "admin",
- "админ" => "admin",
- "менеджер" => "manager",
- "manager" => "manager",
- "клиент" => "client",
- _ => "client"
- };
- }
- }
- --------------------------------------------------------------------------------
- ФАЙЛ: Views/LoginWindow.axaml
- --------------------------------------------------------------------------------
- <Window xmlns="https://github.com/avaloniaui"
- x:Class="AvaloniaApplication1.Views.LoginWindow"
- Width="420" Height="380"
- Icon="/Assets/Icon.ico"
- Title="Авторизация"
- WindowStartupLocation="CenterScreen">
- <Grid>
- <Border Background="{DynamicResource PrimaryBackgroundBrush}" CornerRadius="12" Margin="20">
- <StackPanel Margin="25" Spacing="14" VerticalAlignment="Center">
- <!-- Logo -->
- <Border Background="{DynamicResource SecondaryBackgroundBrush}" CornerRadius="8" Padding="12" HorizontalAlignment="Center">
- <Image Source="/Assets/Icon.JPG" Width="64" Height="54" Stretch="Uniform"/>
- </Border>
- <!-- Title -->
- <TextBlock Text="Вход в систему"
- FontSize="22"
- FontWeight="Bold"
- HorizontalAlignment="Center"
- Foreground="{DynamicResource AccentBrush}"/>
- <!-- Login -->
- <TextBox x:Name="loginBox"
- Watermark="Логин"
- Height="36"
- CornerRadius="6"/>
- <!-- Password -->
- <TextBox x:Name="passwordBox"
- PasswordChar="*"
- Watermark="Пароль"
- Height="36"
- CornerRadius="6"/>
- <!-- Error -->
- <TextBlock x:Name="errorText"
- Foreground="#FF4444"
- TextWrapping="Wrap"
- FontSize="13"
- HorizontalAlignment="Center"/>
- <!-- Login Button -->
- <Button Content="Войти"
- Click="Login_Click"
- HorizontalAlignment="Stretch"
- Height="38"
- CornerRadius="6"
- FontSize="15"
- FontWeight="Bold"
- IsDefault="True"/>
- <!-- Guest Button -->
- <Button Content="Войти как гость"
- Click="Guest_Click"
- HorizontalAlignment="Stretch"
- Height="36"
- CornerRadius="6"
- FontSize="13"/>
- </StackPanel>
- </Border>
- </Grid>
- </Window>
- --------------------------------------------------------------------------------
- ФАЙЛ: Views/LoginWindow.axaml.cs
- --------------------------------------------------------------------------------
- using System;
- using Avalonia;
- using Avalonia.Controls;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Interactivity;
- using AvaloniaApplication1.Models;
- using AvaloniaApplication1.Services;
- namespace AvaloniaApplication1.Views;
- public partial class LoginWindow : Window
- {
- private readonly DbService _dbService = new();
- public LoginWindow()
- {
- InitializeComponent();
- }
- private void Login_Click(object? sender, RoutedEventArgs e)
- {
- errorText.Text = string.Empty;
- var login = loginBox.Text?.Trim() ?? string.Empty;
- var password = passwordBox.Text?.Trim() ?? string.Empty;
- if (string.IsNullOrWhiteSpace(login) || string.IsNullOrWhiteSpace(password))
- {
- errorText.Text = "Введите логин и пароль.";
- return;
- }
- try
- {
- var user = _dbService.Login(login, password);
- if (user is null)
- {
- errorText.Text = "Неверный логин или пароль.";
- return;
- }
- OpenRoleMenu(user);
- }
- catch (Exception ex)
- {
- errorText.Text = $"Ошибка подключения к БД: {ex.Message}";
- }
- }
- private void Guest_Click(object? sender, RoutedEventArgs e)
- {
- errorText.Text = string.Empty;
- OpenRoleMenu(new User
- {
- Login = "guest",
- FullName = "Гость",
- Role = "client"
- });
- }
- private void OpenRoleMenu(User user)
- {
- var roleWindow = new RoleWindow(user, _dbService);
- if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- {
- desktop.MainWindow = roleWindow;
- }
- roleWindow.Show();
- Close();
- }
- }
- --------------------------------------------------------------------------------
- ФАЙЛ: Views/RoleWindow.axaml
- --------------------------------------------------------------------------------
- <Window xmlns="https://github.com/avaloniaui"
- x:Class="AvaloniaApplication1.Views.RoleWindow"
- Width="420" Height="280"
- Icon="/Assets/Icon.ico"
- Title="Главное меню"
- WindowStartupLocation="CenterScreen">
- <StackPanel Margin="25" Spacing="14" VerticalAlignment="Center">
- <!-- Приветствие -->
- <TextBlock x:Name="helloText"
- FontSize="20"
- FontWeight="Bold"
- HorizontalAlignment="Center"/>
- <!-- Твоя роль -->
- <TextBlock x:Name="roleText"
- FontSize="14"
- Foreground="#666"
- HorizontalAlignment="Center"/>
- <!-- Просмотр товаров -->
- <Button Content="Просмотр товаров"
- Click="OpenProducts_Click"
- HorizontalAlignment="Stretch"
- Height="40"
- FontSize="15"/>
- <!-- Выход -->
- <Button Content="Выход"
- Click="Exit_Click"
- HorizontalAlignment="Stretch"
- Height="36"
- FontSize="13"/>
- </StackPanel>
- </Window>
- --------------------------------------------------------------------------------
- ФАЙЛ: Views/RoleWindow.axaml.cs
- --------------------------------------------------------------------------------
- using Avalonia;
- using Avalonia.Controls;
- using Avalonia.Controls.ApplicationLifetimes;
- using Avalonia.Interactivity;
- using AvaloniaApplication1.Models;
- using AvaloniaApplication1.Services;
- namespace AvaloniaApplication1.Views;
- public partial class RoleWindow : Window
- {
- private readonly User _user;
- private readonly DbService _dbService;
- public RoleWindow(User user, DbService dbService)
- {
- _user = user;
- _dbService = dbService;
- InitializeComponent();
- string name = _user.FullName.Trim();
- if (name == "") name = _user.Login;
- helloText.Text = $"Привет, {name}!";
- roleText.Text = $"Роль: {RoleName(_user.Role)}";
- }
- private async void OpenProducts_Click(object? sender, RoutedEventArgs e)
- {
- await new ProductsWindow(_user, _dbService).ShowDialog(this);
- }
- private void Exit_Click(object? sender, RoutedEventArgs e)
- {
- var login = new LoginWindow();
- if (Application.Current?.ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
- desktop.MainWindow = login;
- login.Show();
- Close();
- }
- private static string RoleName(string role) => role switch
- {
- "admin" => "Администратор",
- "manager" => "Менеджер",
- _ => "Клиент"
- };
- }
- --------------------------------------------------------------------------------
- ФАЙЛ: Views/ProductsWindow.axaml
- --------------------------------------------------------------------------------
- <Window xmlns="https://github.com/avaloniaui"
- x:Class="AvaloniaApplication1.Views.ProductsWindow"
- Width="950" Height="650"
- Icon="/Assets/Icon.ico"
- Title="Товары"
- WindowStartupLocation="CenterOwner">
- <Grid RowDefinitions="Auto,*,Auto" Margin="12">
- <!-- Шапка -->
- <Border Background="{DynamicResource SecondaryBackgroundBrush}" CornerRadius="8" Padding="12" Margin="0,0,0,10">
- <TextBlock x:Name="titleText" FontSize="20" FontWeight="Bold" HorizontalAlignment="Center"/>
- </Border>
- <!-- Карточки товаров -->
- <ScrollViewer Grid.Row="1">
- <ItemsControl x:Name="productsControl">
- <ItemsControl.ItemsPanel>
- <ItemsPanelTemplate>
- <WrapPanel Orientation="Horizontal"/>
- </ItemsPanelTemplate>
- </ItemsControl.ItemsPanel>
- <ItemsControl.ItemTemplate>
- <DataTemplate>
- <Border Background="{DynamicResource PrimaryBackgroundBrush}"
- BorderBrush="{DynamicResource AccentBrush}"
- BorderThickness="1"
- CornerRadius="8"
- Width="200"
- Margin="6"
- Padding="8">
- <StackPanel Spacing="4">
- <!-- Фото -->
- <Border CornerRadius="6" Background="#F0F0F0" Height="120" HorizontalAlignment="Center" Width="120">
- <Image Source="{Binding Photo}" Width="110" Height="110" Stretch="Uniform"
- HorizontalAlignment="Center" VerticalAlignment="Center"/>
- </Border>
- <!-- Артикул -->
- <TextBlock Text="{Binding Article, StringFormat='Артикул: {0}'}" FontWeight="Bold" FontSize="12"/>
- <!-- Описание -->
- <TextBlock Text="{Binding Description}" TextWrapping="Wrap" FontSize="11" Foreground="#666" MaxHeight="40"/>
- <Separator/>
- <!-- Цена -->
- <TextBlock Text="{Binding Price, StringFormat='Цена: {0:F2} ₽'}" FontSize="12"/>
- <!-- Скидка -->
- <TextBlock Text="{Binding Discount, StringFormat='Скидка: {0}%'}" Foreground="#FF4444" FontWeight="Bold" FontSize="11"/>
- <!-- Итог -->
- <TextBlock Text="{Binding FinalPrice, StringFormat='Итог: {0:F2} ₽'}" FontWeight="Bold" FontSize="13"
- Foreground="{DynamicResource AccentBrush}"/>
- </StackPanel>
- </Border>
- </DataTemplate>
- </ItemsControl.ItemTemplate>
- </ItemsControl>
- </ScrollViewer>
- <!-- Кнопка назад -->
- <Button Grid.Row="2" Content="Назад" Click="Back_Click" HorizontalAlignment="Left" Margin="0,10,0,0"/>
- </Grid>
- </Window>
- --------------------------------------------------------------------------------
- ФАЙЛ: Views/ProductsWindow.axaml.cs
- --------------------------------------------------------------------------------
- using System;
- using System.Collections.Generic;
- using Avalonia.Controls;
- using Avalonia.Interactivity;
- using AvaloniaApplication1.Models;
- using AvaloniaApplication1.Services;
- namespace AvaloniaApplication1.Views;
- public partial class ProductsWindow : Window
- {
- private readonly User _user;
- private readonly DbService _dbService;
- public ProductsWindow(User user, DbService dbService)
- {
- _user = user;
- _dbService = dbService;
- InitializeComponent();
- titleText.Text = $"Товары ({RoleTitle(_user.Role)})";
- LoadProducts();
- }
- private void LoadProducts()
- {
- try
- {
- var products = _dbService.GetProducts();
- productsControl.ItemsSource = products;
- }
- catch (Exception ex)
- {
- ShowError(ex.Message);
- }
- }
- private void Back_Click(object? sender, RoutedEventArgs e)
- {
- Close();
- }
- private async void ShowError(string message)
- {
- var dialog = new Window
- {
- Width = 400, Height = 150, Title = "Ошибка",
- Content = new StackPanel
- {
- Margin = new Avalonia.Thickness(16),
- Spacing = 12,
- Children =
- {
- new TextBlock { Text = message, TextWrapping = Avalonia.Media.TextWrapping.Wrap },
- new Button { Content = "OK", HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Right }
- }
- }
- };
- ((Button)((StackPanel)dialog.Content).Children[1]).Click += (_, _) => dialog.Close();
- await dialog.ShowDialog(this);
- }
- private static string RoleTitle(string role) => role switch
- {
- "admin" => "админ",
- "manager" => "менеджер",
- _ => "клиент"
- };
- }
- ================================================================================
- КОНЕЦ ВСЕХ ФАЙЛОВ
- ================================================================================
RAW Paste Data
Copied
