Добавьте файлы проекта.

master
ultrageese 2026-03-30 11:27:21 +10:00
parent c66a846d2a
commit 7818c3a4f7
27 changed files with 766 additions and 0 deletions

25
AutoService.sln Normal file
View File

@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36623.8 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoService", "AutoService\AutoService.csproj", "{5845584C-1904-4184-B773-BCE9FE08B4CA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5845584C-1904-4184-B773-BCE9FE08B4CA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5845584C-1904-4184-B773-BCE9FE08B4CA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5845584C-1904-4184-B773-BCE9FE08B4CA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5845584C-1904-4184-B773-BCE9FE08B4CA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7DAD94C8-56AB-4CA7-B7DE-8C85BB9DA95F}
EndGlobalSection
EndGlobal

11
AutoService/App.axaml Normal file
View File

@ -0,0 +1,11 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AutoService.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml"/>
</Application.Styles>
</Application>

55
AutoService/App.axaml.cs Normal file
View File

@ -0,0 +1,55 @@
using AutoService.ViewModels;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Linq;
namespace AutoService
{
public partial class App : Application
{
private readonly IServiceProvider _provider;
public App(IServiceProvider provider)
{
_provider = provider;
}
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
// Avoid duplicate validations from both Avalonia and the CommunityToolkit.
// More info: https://docs.avaloniaui.net/docs/guides/development-guides/data-validation#manage-validationplugins
DisableAvaloniaDataAnnotationValidation();
var win = _provider.GetRequiredService<MainWindow>();
var vm = _provider.GetRequiredService<MainWindowVM>();
win.DataContext = vm;
desktop.MainWindow = win;
}
base.OnFrameworkInitializationCompleted();
}
private void DisableAvaloniaDataAnnotationValidation()
{
// Get an array of plugins to remove
var dataValidationPluginsToRemove =
BindingPlugins.DataValidators.OfType<DataAnnotationsValidationPlugin>().ToArray();
// remove each entry found
foreach (var plugin in dataValidationPluginsToRemove)
{
BindingPlugins.DataValidators.Remove(plugin);
}
}
}
}

View File

@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.3.11" />
<PackageReference Include="Avalonia.Controls.DataGrid" Version="11.3.11" />
<PackageReference Include="Avalonia.Desktop" Version="11.3.11" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.11" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.11" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Include="Avalonia.Diagnostics" Version="11.3.11">
<IncludeAssets Condition="'$(Configuration)' != 'Debug'">None</IncludeAssets>
<PrivateAssets Condition="'$(Configuration)' != 'Debug'">All</PrivateAssets>
</PackageReference>
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.1" />
<PackageReference Include="MessageBox.Avalonia" Version="3.3.1.1" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
<PackageReference Include="MySqlConnector" Version="2.5.0" />
</ItemGroup>
<ItemGroup>
<Compile Update="Views\ReceiptWindow.axaml.cs">
<DependentUpon>ReceiptWindow.axaml</DependentUpon>
</Compile>
<Compile Update="Views\WorksWindow.axaml.cs">
<DependentUpon>WorksWindow.axaml</DependentUpon>
</Compile>
</ItemGroup>
</Project>

View File

@ -0,0 +1,52 @@
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Options;
using MySqlConnector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public class BaseRepository
{
public readonly DatabaseSettings _databaseSetting;
protected MySqlConnection connection;
public BaseRepository(IOptions<DatabaseSettings> databaseSettings)
{
_databaseSetting = databaseSettings.Value;
connection = new MySqlConnection(_databaseSetting.ConnectionString);
}
public bool OpenConnection()
{
try
{
connection.Open();
return true;
}catch(Exception e)
{
Console.WriteLine(e);
return false;
}
}
public bool CloseConnection()
{
try
{
connection.Close();
return true;
}
catch(Exception e)
{
Console.WriteLine(e);
return false;
}
}
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public class DatabaseSettings
{
public string ConnectionString { get; set; }
}
}

View File

@ -0,0 +1,13 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public interface IServiceRepository
{
IEnumerable<Service> GetAll();
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public interface IWorkRepository
{
IEnumerable<Work> GetByServiceId(int serviceId);
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public class LocalContainer
{
public Dictionary<string, string> StringValues { get; set; } = new();
public Service SelectedService { get; set; }
public ObservableCollection<Work> Works { get; set; }
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public class Service
{
public int Id { get; set; }
public string Title { get; set; }
public string Description { get; set; }
}
}

View File

@ -0,0 +1,46 @@
using Microsoft.Extensions.Options;
using MySqlConnector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public class ServiceRepository : BaseRepository, IServiceRepository
{
public ServiceRepository(IOptions<DatabaseSettings> databaseSettings) : base(databaseSettings)
{
}
public IEnumerable<Service> GetAll()
{
string sql = "SELECT * FROM services";
List<Service> result = new();
try
{
OpenConnection();
using var mc = new MySqlCommand(sql, connection);
using var reader = mc.ExecuteReader();
while (reader.Read())
{
result.Add(new Service()
{
Id = reader.GetInt32("id"),
Title = reader.GetString("title"),
Description = reader.GetString("description")
});
}
CloseConnection();
}
catch (Exception e)
{
Console.WriteLine(e);
}
return result;
}
}
}

View File

@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public class Work
{
public string Title { get; set; }
public int Price { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public class WorkIsChecked
{
public bool IsChecked { get; set; }
public Work Work { get; set; }
}
}

View File

@ -0,0 +1,45 @@
using Microsoft.Extensions.Options;
using MySqlConnector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.Models
{
public class WorkRepository : BaseRepository, IWorkRepository
{
public WorkRepository(IOptions<DatabaseSettings> databaseSettings) : base(databaseSettings)
{
}
public IEnumerable<Work> GetByServiceId(int serviceId)
{
string sql = "SELECT work_name, price FROM works WHERE service_id=@SID";
List<Work> result = new();
try
{
OpenConnection();
using var mc = new MySqlCommand(sql, connection);
mc.Parameters.AddWithValue("@SID", serviceId);
using var reader = mc.ExecuteReader();
while (reader.Read())
{
result.Add(new Work()
{
Title = reader.GetString("work_name"),
Price = reader.GetInt32("price")
});
}
CloseConnection();
}catch(Exception e)
{
Console.WriteLine(e);
}
return result;
}
}
}

50
AutoService/Program.cs Normal file
View File

@ -0,0 +1,50 @@
using AutoService.Models;
using AutoService.ViewModels;
using Avalonia;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
namespace AutoService
{
internal class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args)
{
var host = Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.SetBasePath(AppContext.BaseDirectory).AddJsonFile("appsettings.json").AddEnvironmentVariables();
})
.ConfigureServices((c,s) =>
{
s.Configure<DatabaseSettings>(c.Configuration.GetSection("DatabaseSettings"));
s.AddTransient<MainWindowVM>();
s.AddTransient<MainWindow>();
s.AddTransient<WorksWindow>();
s.AddTransient<WorksWindowVM>();
s.AddTransient<ReceiptWindowVM>();
s.AddTransient<ReceiptWindow>();
s.AddSingleton<IWorkRepository,WorkRepository>();
s.AddSingleton<IServiceRepository, ServiceRepository>();
s.AddSingleton<LocalContainer>();
}).Build();
BuildAvaloniaApp(host.Services)
.StartWithClassicDesktopLifetime(args);
}
// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp(IServiceProvider provider)
=> AppBuilder.Configure(() => new App(provider))
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
}

View File

@ -0,0 +1,89 @@
using AutoService.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using MsBox.Avalonia;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.ViewModels
{
public partial class MainWindowVM:ViewModelBase
{
private readonly IServiceProvider _serviceProvider;
private readonly IServiceRepository _serviceRepository;
private readonly LocalContainer _container;
[ObservableProperty]
private ObservableCollection<Service> services;
[ObservableProperty]
private string clientName;
[ObservableProperty]
private string autoModel;
[ObservableProperty]
private Service selectedService;
public MainWindowVM(IServiceProvider serviceProvider, IServiceRepository serviceRepository, LocalContainer localContainer)
{
_serviceProvider = serviceProvider;
_serviceRepository = serviceRepository;
_container = localContainer;
ClientName = "";
AutoModel = "";
try
{
services = new ObservableCollection<Service>(_serviceRepository.GetAll());
SelectedService = Services.First();
}
catch(Exception e)
{
Console.WriteLine(e);
services = new();
}
}
[RelayCommand]
public async Task StartWorksWindowAsync()
{
if (ClientName.Length > 2 && AutoModel.Length > 2)
{
if (!_container.StringValues.ContainsKey("ClientName"))
{
_container.StringValues.Add("ClientName", ClientName);
}
else
{
_container.StringValues["ClientName"] = ClientName;
}
if (!_container.StringValues.ContainsKey("AutoModel"))
{
_container.StringValues.Add("AutoModel", AutoModel);
}
else
{
_container.StringValues["AutoModel"] = AutoModel;
}
var vm = _serviceProvider.GetRequiredService<WorksWindowVM>();
var win = _serviceProvider.GetRequiredService<WorksWindow>();
win.DataContext = vm;
win.Show();
}
else
{
var errWin = MessageBoxManager.GetMessageBoxStandard("Косяк!", "Поля \"Имя\" и \"Марка машины\" должны быть заполнены!", MsBox.Avalonia.Enums.ButtonEnum.Ok);
await errWin.ShowAsync();
}
}
}
}

View File

@ -0,0 +1,47 @@
using AutoService.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.ViewModels
{
public partial class ReceiptWindowVM: ViewModelBase
{
private readonly IServiceProvider _serviceProvider;
private readonly LocalContainer _localContainer;
[ObservableProperty]
private ObservableCollection<Work> works;
[ObservableProperty]
private int sumPrice = 0;
private double discount = 0;
[ObservableProperty]
private string discountText;
[ObservableProperty]
private double totalPrice;
public ReceiptWindowVM(IServiceProvider serviceProvider, ObservableCollection<Work> works, LocalContainer localContainer)
{
_serviceProvider = serviceProvider;
_localContainer = localContainer;
Works = works;
foreach (var item in Works) sumPrice += item.Price;
if (sumPrice >= 10_000) discount = 0.1;
else if (sumPrice >= 5_000) discount = 0.05;
DiscountText = (discount * 100).ToString() + "%";
TotalPrice = sumPrice * (1-discount);
}
}
}

View File

@ -0,0 +1,13 @@
using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.ViewModels
{
public class ViewModelBase: ObservableObject
{
}
}

View File

@ -0,0 +1,57 @@
using AutoService.Models;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AutoService.ViewModels
{
public partial class WorksWindowVM:ViewModelBase
{
private readonly IServiceProvider _serviceProvider;
private readonly LocalContainer _localContainer;
private Service _service;
[ObservableProperty]
private ObservableCollection<WorkIsChecked> works;
public WorksWindowVM(IServiceProvider serviceProvider, IWorkRepository workRepository, LocalContainer container)
{
_serviceProvider = serviceProvider;
_service = container.SelectedService;
_localContainer = container;
Works = new ObservableCollection<WorkIsChecked>();
foreach(Work work in workRepository.GetByServiceId(_service.Id))
{
Works.Add(new WorkIsChecked
{
Work = work,
IsChecked = false
});
}
}
[RelayCommand]
public void CalculateCount()
{
ObservableCollection<Work> selectedWorks = new();
foreach(var work in Works)
{
if (work.IsChecked) selectedWorks.Add(work.Work);
}
var vm = ActivatorUtilities.CreateInstance<ReceiptWindowVM>(_serviceProvider, selectedWorks, _localContainer);
var win = _serviceProvider.GetRequiredService<ReceiptWindow>();
win.DataContext = vm;
win.Show();
}
}
}

View File

@ -0,0 +1,23 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:AutoService.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AutoService.MainWindow"
Title="AutoService"
x:DataType="vm:MainWindowVM">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="30" Width="200">
<TextBox Watermark="Имя клиента" Margin="10" Text="{Binding ClientName}"/>
<TextBox Watermark="Модель авто" Margin="10" Text="{Binding AutoModel}"/>
<ComboBox Margin="10" ItemsSource="{Binding Services}" HorizontalAlignment="Stretch" SelectedItem="{Binding SelectedService}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" ToolTip.Tip="{Binding Description}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<Button Content="Перейти к работам" Margin="10" Command="{Binding StartWorksWindowCommand}"/>
</StackPanel>
</Window>

View File

@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace AutoService
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,33 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:AutoService.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AutoService.ReceiptWindow"
Title="AutoService"
x:DataType="vm:ReceiptWindowVM">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="30">
<DataGrid ItemsSource="{Binding Works}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Title}" Header="Название"/>
<DataGridTextColumn Binding="{Binding Price}" Header="Цена"/>
</DataGrid.Columns>
</DataGrid>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Сумма работ: "/>
<TextBlock Text="{Binding SumPrice}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Скидка: "/>
<TextBlock Text="{Binding DiscountText}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Итого к оплате: "/>
<TextBlock Text="{Binding TotalPrice}"/>
</StackPanel>
</StackPanel>
</Window>

View File

@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace AutoService
{
public partial class ReceiptWindow : Window
{
public ReceiptWindow()
{
InitializeComponent();
}
}
}

View File

@ -0,0 +1,22 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:models="using:AutoService.Models"
xmlns:vm="clr-namespace:AutoService.ViewModels"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="AutoService.WorksWindow"
Title="AutoService"
x:DataType="vm:WorksWindowVM">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Margin="30">
<DataGrid ItemsSource="{Binding Works}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Название" Binding="{Binding Work.Title}"/>
<DataGridTextColumn Header="Цена" Binding="{Binding Work.Price}"/>
<DataGridCheckBoxColumn Header="Выполнено" Binding="{Binding IsChecked}"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="Рассчитать стоимость" Command="{Binding CalculateCountCommand}"/>
</StackPanel>
</Window>

View File

@ -0,0 +1,13 @@
using AutoService.ViewModels;
using Avalonia.Controls;
namespace AutoService
{
public partial class WorksWindow : Window
{
public WorksWindow()
{
InitializeComponent();
}
}
}

18
AutoService/app.manifest Normal file
View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="AutoService.Desktop"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>

View File

@ -0,0 +1,5 @@
{
"DatabaseSettings": {
"ConnectionString": "server=192.168.200.13;user=student;password=student;database=auto_service_db"
}
}