================================================================================ ПРОЕКТ: AvaloniaApplication1 (Магазин обуви Botinki) ================================================================================ -------------------------------------------------------------------------------- ФАЙЛ: AvaloniaApplication1.csproj -------------------------------------------------------------------------------- WinExe net10.0 enable app.manifest AvaloniaApplication1 PreserveNewest -------------------------------------------------------------------------------- ФАЙЛ: 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() .UsePlatformDetect() .LogToTrace(); } -------------------------------------------------------------------------------- ФАЙЛ: App.axaml -------------------------------------------------------------------------------- #FFFFFF #7FFF00 #00FA9A -------------------------------------------------------------------------------- ФАЙЛ: 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 GetProducts() { var list = new List(); 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 --------------------------------------------------------------------------------