================================================================================
ПРОЕКТ: 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
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
ФАЙЛ: 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
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
ФАЙЛ: 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
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
ФАЙЛ: 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" => "менеджер",
_ => "клиент"
};
}
================================================================================
КОНЕЦ ВСЕХ ФАЙЛОВ
================================================================================