This commit is contained in:
2026-04-02 13:30:53 +05:00
parent dc35ce3325
commit 4ba3e2d29e
9 changed files with 259 additions and 57 deletions

View File

@@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace UP01Task2App.Models
{
public class Client
{
public string Surname { get; set; }
public string Name { get; set; }
public string Patronymic { get; set; }
public DateTime? BirthDate { get; set; }
public string Adress { get; set; }
public string PhoneNumber { get; set; }
public int SubscriptionNumber { get; set; }
public override string ToString()
{
return Surname + " " + Name + " " + Patronymic;
}
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace UP01Task2App.Models
{
public class Record
{
public Client Client { get; set; }
public Trainer Trainer { get; set; }
DateOnly RecordDate { set; get; }
TimeOnly RecordTime { set; get; }
}
}

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace UP01Task2App.Models
{
public class Trainer
{
public string Surmname { get; set; }
public string Name { get; set; }
public string Patronymic { get; set; }
public string Speciality { get; set; }
}
}

View File

@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Windows;
using System.Windows.Shapes;
using UP01Task2App.Models;
namespace UP01Task2App.Services
{
internal class JsonService
{
private const string trainerPath = "data/trainers.json";
private const string clientPath = "data/clients.json";
private const string recordPath = "data/records.json";
private void SaveToJson<T>(string path, List<T> data)
{
var options = new JsonSerializerOptions
{
WriteIndented = true
};
try
{
data ??= new List<T>();
string json = JsonSerializer.Serialize(data, options);
string? dir = System.IO.Path.GetDirectoryName(path);
if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
Directory.CreateDirectory(dir);
File.WriteAllText(path, json, Encoding.UTF8);
}
catch
{
throw new ArgumentException("Не удалось сохранить данные");
}
}
private List<T> ReadFromJson<T>(string path)
{
if (!File.Exists(path) || new FileInfo(path).Length == 0)
return new List<T>();
string json = File.ReadAllText(path);
if (string.IsNullOrWhiteSpace(json))
return new List<T>();
try
{
return JsonSerializer.Deserialize<List<T>>(json) ?? new List<T>();
}
catch
{
return new List<T>();
}
}
public List<Trainer> ReadTrainerDataFromJson() => ReadFromJson<Trainer>(trainerPath);
public List<Client> ReadClientDataFromJson() => ReadFromJson<Client>(clientPath);
public List<Record> ReadRecordDataFromJson() => ReadFromJson<Record>(recordPath);
public void SaveTrainerDataToJson(List<Trainer> trainersList) => SaveToJson(trainerPath, trainersList);
public void SaveClientDataToJson(List<Client> clientsList) => SaveToJson(clientPath, clientsList);
public void SaveRecordDataToJson(List<Record> recordsList) => SaveToJson(recordPath, recordsList);
}
}

View File

@@ -0,0 +1,39 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Controls;
namespace UP01Task2App.Services
{
public static class ValidationHelper
{
public static void ValidateNotNullTextBox(TextBox obj, string fieldName)
{
if (string.IsNullOrWhiteSpace(obj.Text))
throw new ArgumentException($"Поле \"{fieldName}\" не должно быть пустым");
}
public static void ValidateNumTextBox(TextBox obj, string fieldName)
{
foreach (var c in obj.Text)
{
if (!char.IsDigit(c))
throw new ArgumentException($"В поле \"{fieldName}\" можно вводить только числа");
}
}
public static void ValidateDateNotInFutureTextBox(DatePicker obj, string fieldName)
{
if (obj.SelectedDate > DateTime.Now)
throw new ArgumentException($"В поле \"{fieldName}\" выбранная дата не может быть в будущем");
}
public static void ValidateNotNullDatePicker(DatePicker obj, string fieldName)
{
if (obj.SelectedDate == null)
throw new ArgumentException($"Выберите дату в поле \"{fieldName}\"");
}
public static void ValidateIntRangeTextBox(TextBox obj, string fieldName)
{
if (!int.TryParse(obj.Text, out int _))
throw new ArgumentException($"Введенное число в \"{fieldName}\" слишком большое или маленькое");
}
}
}

View File

@@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace UP01Task2App.ViewModels
{
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}

View File

@@ -1,20 +0,0 @@
using System;
using System.Windows.Input;
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
public void Execute(object parameter) => _execute();
public event EventHandler CanExecuteChanged;
}

View File

@@ -11,32 +11,32 @@
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<StackPanel HorizontalAlignment="Left"> <StackPanel HorizontalAlignment="Left">
<TextBlock Text="Фамилия"/> <TextBlock Text="Фамилия"/>
<TextBox Text="{Binding LastName}" Style="{StaticResource TextBoxStyle}"/> <TextBox Name="SurnameTextBox" Style="{StaticResource TextBoxStyle}"/>
<TextBlock Text="Имя"/> <TextBlock Text="Имя"/>
<TextBox Text="{Binding FirstName}" Style="{StaticResource TextBoxStyle}"/> <TextBox Name="NameTextBox" Style="{StaticResource TextBoxStyle}"/>
<TextBlock Text="Отчество"/> <TextBlock Text="Отчество"/>
<TextBox Text="{Binding MiddleName}" Style="{StaticResource TextBoxStyle}"/> <TextBox Name="PatronymicTextBox" Style="{StaticResource TextBoxStyle}"/>
<TextBlock Text="Дата рождения"/> <TextBlock Text="Дата рождения"/>
<DatePicker SelectedDate="{Binding BirthDate}" Style="{StaticResource DatePickerStyle}" /> <DatePicker Name="BirthDateDatePicker" Style="{StaticResource DatePickerStyle}" />
</StackPanel> </StackPanel>
<StackPanel HorizontalAlignment="Right" Margin="20,0,0,0"> <StackPanel HorizontalAlignment="Right" Margin="20,0,0,0">
<TextBlock Text="Фактический адрес"/> <TextBlock Text="Фактический адрес"/>
<TextBox Text="{Binding Address}" Style="{StaticResource TextBoxStyle}"/> <TextBox Name="AdressTextBox" Style="{StaticResource TextBoxStyle}"/>
<TextBlock Text="Телефон"/> <TextBlock Text="Телефон"/>
<TextBox Text="{Binding Phone}" Style="{StaticResource TextBoxStyle}"/> <TextBox Name="PhoneNumberTextBox" Style="{StaticResource TextBoxStyle}"/>
<TextBlock Text="Номер абонемента"/> <TextBlock Text="Номер абонемента"/>
<TextBox Text="{Binding MembershipNumber}" Style="{StaticResource TextBoxStyle}"/> <TextBox Name="SubscriptionMumberTextBox" Style="{StaticResource TextBoxStyle}"/>
<TextBlock Text="Пол"/> <TextBlock Text="Пол"/>
<StackPanel Orientation="Horizontal" Margin="5"> <StackPanel Orientation="Horizontal" Margin="5">
<RadioButton Content="М" GroupName="rg1"/> <RadioButton Name="rbMan" Content="М" GroupName="rg1"/>
<RadioButton Content="Ж" GroupName="rg1"/> <RadioButton Name="rbWoman" Content="Ж" GroupName="rg1"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
@@ -47,33 +47,27 @@
<TextBlock Text="Список клиентов" FontSize="18" Margin="0,0,0,10"/> <TextBlock Text="Список клиентов" FontSize="18" Margin="0,0,0,10"/>
<ListBox ItemsSource="{Binding Clients}" <ListBox ItemsSource="{Binding ClientsUI}"
SelectedItem="{Binding SelectedClient}" Height="250"/>
Height="250"
DisplayMemberPath="LastName"/>
<StackPanel Orientation="Horizontal" <StackPanel Orientation="Horizontal"
Margin="10"> Margin="10">
<StackPanel> <StackPanel>
<Button Content="Добавить клиента" <Button Content="Добавить клиента"
Command="{Binding AddClientCommand}"
Style="{StaticResource ButtonStyle}" Style="{StaticResource ButtonStyle}"
Margin="10"/> Margin="10" Click="Button_Click"/>
<Button Content="Изменить клиента" <Button Content="Изменить клиента"
Command="{Binding EditClientCommand}"
Style="{StaticResource ButtonStyle}" Style="{StaticResource ButtonStyle}"
Margin="10"/> Margin="10"/>
</StackPanel> </StackPanel>
<StackPanel> <StackPanel>
<Button Content="Запись клиента" <Button Content="Запись клиента"
Command="{Binding AddAppointmentCommand}"
Style="{StaticResource ButtonStyle}" Style="{StaticResource ButtonStyle}"
Margin="10"/> Margin="10"/>
<Button Content="Просмотреть записи" <Button Content="Просмотреть записи"
Command="{Binding ViewAppointmentsCommand}"
Style="{StaticResource ButtonStyle}" Style="{StaticResource ButtonStyle}"
Margin="10"/> Margin="10"/>
</StackPanel> </StackPanel>

View File

@@ -1,6 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Text; using System.Text;
using System.Text.Json;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Data; using System.Windows.Data;
@@ -9,6 +12,8 @@ using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Shapes; using System.Windows.Shapes;
using UP01Task2App.Models;
using UP01Task2App.Services;
namespace UP01Task2App.Windows namespace UP01Task2App.Windows
{ {
@@ -17,9 +22,91 @@ namespace UP01Task2App.Windows
/// </summary> /// </summary>
public partial class ClientWindow : Window public partial class ClientWindow : Window
{ {
private JsonService Json = new();
public List<Client> ClientsList = new();
public List<Trainer> TrainersList = new();
public List<Record> RecordsList = new();
public ObservableCollection<Client> ClientsUI { get; set; } = new();
public ClientWindow() public ClientWindow()
{ {
InitializeComponent(); InitializeComponent();
ClientsList = Json.ReadClientDataFromJson();
TrainersList = Json.ReadTrainerDataFromJson();
RecordsList = Json.ReadRecordDataFromJson();
UpdateClientUI();
this.DataContext = this;
}
private void UpdateClientUI()
{
ClientsUI.Clear();
foreach (var item in ClientsList)
{
ClientsUI.Add(item);
}
}
private bool ValidateFields()
{
try
{
ValidationHelper.ValidateNotNullTextBox(SurnameTextBox, "Фамилия");
ValidationHelper.ValidateNotNullTextBox(NameTextBox, "Имя");
ValidationHelper.ValidateNotNullTextBox(PatronymicTextBox, "Отчество");
ValidationHelper.ValidateNotNullDatePicker(BirthDateDatePicker, "Дата рождения");
ValidationHelper.ValidateDateNotInFutureTextBox(BirthDateDatePicker, "Дата рождения");
ValidationHelper.ValidateNotNullTextBox(AdressTextBox, "Фактический адрес");
ValidationHelper.ValidateNotNullTextBox(PhoneNumberTextBox, "Телефон");
ValidationHelper.ValidateNumTextBox(PhoneNumberTextBox, "Телефон");
ValidationHelper.ValidateNotNullTextBox(SubscriptionMumberTextBox, "Номер абонемента");
ValidationHelper.ValidateNumTextBox(SubscriptionMumberTextBox, "Номер абонемента");
ValidationHelper.ValidateIntRangeTextBox(SubscriptionMumberTextBox, "Номер абонемента");
if (rbMan.IsChecked == null && rbWoman.IsChecked == null)
{
throw new ArgumentException("Выберите пол");
}
return true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
return false;
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
if (ValidateFields())
{
ClientsList.Add(new Client
{
Surname = SurnameTextBox.Text,
Name = NameTextBox.Text,
Adress = AdressTextBox.Text,
BirthDate = BirthDateDatePicker.SelectedDate,
Patronymic = PatronymicTextBox.Text,
PhoneNumber = PhoneNumberTextBox.Text,
SubscriptionNumber = int.Parse(SubscriptionMumberTextBox.Text)
});
try
{
Json.SaveClientDataToJson(ClientsList);
MessageBox.Show("Данные сохранены");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
UpdateClientUI();
}
} }
} }
} }