DB integration added, multiple images for equipment

This commit is contained in:
retr0volkov
2025-07-15 11:14:28 +03:00 Unverified
parent 7a34fc2dbb
commit 8ddce4005f
38 changed files with 627 additions and 297 deletions
+15 -15
View File
@@ -9,18 +9,19 @@ namespace GazpromApp.ViewModels;
public partial class EquipmentViewModel : ViewModelBase
{
private Equipment Equipment { get; }
[ObservableProperty]
private string? _name;
[ObservableProperty]
private string? _description;
[ObservableProperty]
private ImageViewModel? _image;
public ObservableCollection<PropertyViewModel> Properties { get; set; }
public ObservableCollection<InspectionViewModel> Inspections { get; set; }
public ObservableCollection<ImageViewModel> Images { get; init; } = [];
public ObservableCollection<PropertyViewModel> Properties { get; }
public ObservableCollection<InspectionViewModel> Inspections { get; }
[ObservableProperty]
private string _nearestInspection;
@@ -29,17 +30,17 @@ public partial class EquipmentViewModel : ViewModelBase
{
Name = string.Empty;
Description = string.Empty;
Image = null;
Properties = [];
Inspections = [];
NearestInspection = "Нету инспекций";
Equipment = new Equipment();
}
public EquipmentViewModel(Equipment equipment)
{
Name = equipment.Name;
Description = equipment.Description;
Image = new ImageViewModel(equipment.Image ?? new Image());
Images = new ObservableCollection<ImageViewModel>(equipment.Images.Select(i => new ImageViewModel(i)));
Properties = new ObservableCollection<PropertyViewModel>(equipment.Properties.Select(e => new PropertyViewModel(e)));
var times = equipment.Inspections.Select(e => new InspectionViewModel(e))
@@ -57,16 +58,15 @@ public partial class EquipmentViewModel : ViewModelBase
.ToLongDateString();
if (Inspections.Count == 0) NearestInspection = "Нету инспекций";
Equipment = equipment;
}
public Equipment GetEquipment()
{
return new Equipment
{
Name = Name,
Description = Description,
Image = Image?.GetImage(),
Inspections = Inspections.Select(e => e.GetInspection()).ToList()
};
Equipment.Name = Name;
Equipment.Description = Description;
Equipment.Images = Images.Select(e => e.GetImage()).ToList();
Equipment.Inspections = Inspections.Select(e => e.GetInspection()).ToList();
return Equipment;
}
}
+17 -12
View File
@@ -1,5 +1,4 @@
using System.Collections.Generic;
using System.IO;
using System.IO;
using Avalonia.Media.Imaging;
using CommunityToolkit.Mvvm.ComponentModel;
using GazpromApp.Models;
@@ -8,13 +7,19 @@ namespace GazpromApp.ViewModels;
public partial class ImageViewModel : ViewModelBase
{
private Image Image { get; }
[ObservableProperty]
private byte[]? _bytes;
// EF stuff
[ObservableProperty]
private List<Point>? _points;
private Point? _point;
[ObservableProperty]
private List<Equipment>? _equipment;
private Equipment? _equipment;
[ObservableProperty]
private Bitmap? _fileName;
@@ -22,26 +27,26 @@ public partial class ImageViewModel : ViewModelBase
{
Bytes = image.Bytes;
Equipment = image.Equipment;
Points = image.Points;
Point = image.Point;
FileName = image.Bytes is { Length: > 0 } ? new Bitmap(new MemoryStream(image.Bytes)) : null;
Image = image;
}
public ImageViewModel()
{
Bytes = null;
Equipment = null;
Points = null;
Point = null;
Image = new Image();
}
public Image GetImage()
{
var ms = new MemoryStream();
FileName?.Save(ms);
return new Image
{
Bytes = ms.ToArray(),
Equipment = Equipment ?? [],
Points = Points ?? []
};
Image.Bytes = ms.ToArray();
Image.Equipment = Equipment;
Image.Point = Point;
return Image;
}
}
+8 -7
View File
@@ -7,6 +7,7 @@ namespace GazpromApp.ViewModels;
public partial class InspectionViewModel : ViewModelBase
{
private Inspection Inspection { get; }
[ObservableProperty]
private string _name;
[ObservableProperty]
@@ -27,6 +28,7 @@ public partial class InspectionViewModel : ViewModelBase
InspectionDate = DateTimeOffset.Now;
PeriodType = 0;
PeriodValue = 1;
Inspection = new Inspection();
}
public InspectionViewModel(Inspection inspection)
@@ -35,16 +37,15 @@ public partial class InspectionViewModel : ViewModelBase
InspectionDate = inspection.InspectionDate;
PeriodType = inspection.PeriodType.ToPeriodType();
PeriodValue = inspection.PeriodValue;
Inspection = inspection;
}
public Inspection GetInspection()
{
return new Inspection
{
Name = Name,
InspectionDate = InspectionDate,
PeriodType = PeriodType.ToDescription(),
PeriodValue = PeriodValue
};
Inspection.Name = Name;
Inspection.InspectionDate = InspectionDate;
Inspection.PeriodType = PeriodType.ToDescription();
Inspection.PeriodValue = PeriodValue;
return Inspection;
}
}
+12 -11
View File
@@ -6,6 +6,8 @@ namespace GazpromApp.ViewModels;
public partial class PointViewModel : ViewModelBase
{
private Point Point { get; }
[ObservableProperty]
private string? _name;
@@ -20,14 +22,15 @@ public partial class PointViewModel : ViewModelBase
// EF stuff
[ObservableProperty]
private List<Image>? _images;
private List<Image> _images;
[ObservableProperty]
private List<Equipment>? _equipment;
private List<Equipment> _equipment;
[ObservableProperty]
private List<Property>? _properties;
private List<Property> _properties;
public PointViewModel(Point point)
{
Point = point;
Name = point.Name;
Description = point.Description;
Latitude = point.Latitude;
@@ -39,13 +42,11 @@ public partial class PointViewModel : ViewModelBase
public Point GetPoint()
{
return new Point
{
Name = Name,
Description = Description,
Equipment = Equipment ?? [],
Images = Images ?? [],
Properties = Properties ?? []
};
Point.Name = Name;
Point.Description = Description;
Point.Equipment = Equipment;
Point.Images = Images;
Point.Properties = Properties;
return Point;
}
}
+11 -10
View File
@@ -8,6 +8,8 @@ namespace GazpromApp.ViewModels;
public partial class PropertyViewModel : ViewModelBase
{
private Property Property { get; }
[ObservableProperty]
private string _name;
@@ -18,22 +20,18 @@ public partial class PropertyViewModel : ViewModelBase
// Visual and other properties
public PropertyViewModel? Parent { get; set; }
public PropertyViewModel? Parent { get; init; }
[ObservableProperty]
private bool _isEditing;
public Property GetProperty(Property? parent = null)
{
var p = new Property
{
Name = Name,
Value = Value,
Properties = [],
Parent = parent
};
p.Properties = Properties.Select(e => e.GetProperty(p)).ToList();
return p;
Property.Name = Name;
Property.Value = Value;
Property.Parent = parent;
Property.Properties = Properties.Select(e => e.GetProperty(Property)).ToList();
return Property;
}
public PropertyViewModel()
@@ -42,6 +40,7 @@ public partial class PropertyViewModel : ViewModelBase
Value = "Значение свойства";
Properties = [];
Parent = null;
Property = new Property();
}
public PropertyViewModel(Property property, PropertyViewModel? parent = null)
@@ -51,6 +50,7 @@ public partial class PropertyViewModel : ViewModelBase
Properties = new ObservableCollection<PropertyViewModel>(
property.Properties.Select(e => new PropertyViewModel(e, this)));
Parent = property.Parent is null ? null : parent;
Property = property;
}
public PropertyViewModel(PropertyViewModel property)
@@ -59,5 +59,6 @@ public partial class PropertyViewModel : ViewModelBase
Value = "Значение свойства";
Properties = [];
Parent = property;
Property = new Property();
}
}
+30 -34
View File
@@ -32,8 +32,7 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase
[ObservableProperty]
private bool _isDescriptionEditing;
[ObservableProperty]
private ImageViewModel? _image;
public ObservableCollection<ImageViewModel> Images { get; set; }
public ObservableCollection<InspectionViewModel> Inspections { get; set; }
@@ -56,43 +55,39 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase
{
var files = await w.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
AllowMultiple = false,
Title = "Выберите картинку",
AllowMultiple = true,
Title = "Выберите одну или несколько картинок",
FileTypeFilter = [FilePickerFileTypes.ImageAll]
});
IsImageSelected = files.Count > 0;
try
foreach (var file in files)
{
Image = IsImageSelected ? new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(files[0].Path.AbsolutePath)) } : null;
}
catch (ArgumentException)
{
var error = new Error
try
{
Title = "Ошибка обработки картинки",
Message = "Картинка не была добавлена",
Severity = ErrorSeverity.Error
};
_ = await WeakReferenceMessenger.Default.Send(new ErrorMessage(new ErrorViewModel(error)));
IsImageSelected = false;
Images.Add(new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(file.Path.AbsolutePath) ) });
}
catch (ArgumentException)
{
var error = new Error
{
Title = "Ошибка обработки картинки",
Message = $"Картинка не была добавлена: \"{HttpUtility.UrlDecode(file.Name)}\"",
Severity = ErrorSeverity.Error
};
_ = await WeakReferenceMessenger.Default.Send(new ErrorMessage(new ErrorViewModel(error)));
}
}
IsImagesEmpty = Images.Count == 0;
}
[RelayCommand]
private async Task ViewImage()
{
if (Image is null) return;
_ = await WeakReferenceMessenger.Default.Send(new ViewImageMessage(Image));
}
private static async Task ViewImage(ImageViewModel image) => _ = await WeakReferenceMessenger.Default.Send(new ViewImageMessage(image));
[RelayCommand]
private void DeleteImage()
private void DeleteImage(ImageViewModel image)
{
Image = null;
IsImageSelected = false;
Images.Remove(image);
IsImagesEmpty = Images.Count == 0;
}
[RelayCommand]
@@ -146,7 +141,7 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase
{
Name = Name,
Description = Description,
Image = Image?.GetImage(),
Images = Images.Select(e => e.GetImage()).ToList(),
Inspections = Inspections.Select(e => e.GetInspection()).ToList(),
Properties = Properties.Select(e => e.GetProperty()).ToList()
};
@@ -163,13 +158,14 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase
// Visual and other properties
[ObservableProperty]
private bool _isImageSelected;
private bool _isImagesEmpty;
[ObservableProperty]
private bool _isClosed;
private readonly EquipmentViewModel _equipment;
// ReSharper disable once MemberCanBeMadeStatic.Global
public PeriodType[] AllPeriods => Enum.GetValues<PeriodType>()
.Where(p => p != PeriodType.Unknown)
.ToArray();
@@ -178,11 +174,11 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase
{
Name = "Testing";
Description = "Testing";
Image = null;
Images = [];
Inspections = [];
Properties = [];
IsImageSelected = false;
IsImagesEmpty = true;
_equipment = new EquipmentViewModel();
}
@@ -191,11 +187,11 @@ public partial class EditEquipmentDialogViewModel : ViewModelBase
{
Name = equipment.Name;
Description = equipment.Description;
Image = equipment.Image;
Images = equipment.Images;
Inspections = new ObservableCollection<InspectionViewModel>(equipment.Inspections.Select(e => e));
Properties = new ObservableCollection<PropertyViewModel>(equipment.Properties.Select(e => e));
IsImageSelected = equipment.Image is { Bytes.Length: > 0 };
IsImagesEmpty = Images.Count == 0;
_equipment = equipment;
}
+15 -14
View File
@@ -142,15 +142,14 @@ public partial class EditPointDialogViewModel : ViewModelBase
private void CompleteEdit()
{
IsClosed = true;
var point = new Point
{
Name = Name,
Description = Description,
Images = Images.Select(e => e.GetImage()).ToList(),
Equipment = Equipment.Select(e => e.GetEquipment()).ToList(),
Properties = Properties.Select(e => e.GetProperty()).ToList()
};
WeakReferenceMessenger.Default.Send(new EditPointDone(new PointViewModel(point)));
_point.Name = Name;
_point.Description = Description;
_point.Images = Images.Select(e => e.GetImage()).ToList();
_point.Equipment = Equipment.Select(e => e.GetEquipment()).ToList();
_point.Properties = Properties.Select(e => e.GetProperty()).ToList();
_ = _point.GetPoint();
WeakReferenceMessenger.Default.Send(new EditPointDone(new PointViewModel(_point.GetPoint())));
}
[RelayCommand]
@@ -182,12 +181,14 @@ public partial class EditPointDialogViewModel : ViewModelBase
new EquipmentViewModel
{
Name = "Equipment Name Very Long",
Description = "Really big description for the equipment to show the long textReally big description for the equipment to show the long textReally big description for the equipment to show the long textReally big description for the equipment to show the long text", Image = null
Description = "Really big description for the equipment to show the long textReally big description for the equipment to show the long textReally big description for the equipment to show the long textReally big description for the equipment to show the long text",
Images = []
},
new EquipmentViewModel
{
Name = "Equipment Name Very Long",
Description = "Really big description for the equipment to show the long textReally big description for the equipment to show the long textReally big description for the equipment to show the long textReally big description for the equipment to show the long text", Image = null
Description = "Really big description for the equipment to show the long textReally big description for the equipment to show the long textReally big description for the equipment to show the long textReally big description for the equipment to show the long text",
Images = []
}
];
Properties = [
@@ -254,9 +255,9 @@ public partial class EditPointDialogViewModel : ViewModelBase
{
Name = point.Name;
Description = point.Description;
Images = new ObservableCollection<ImageViewModel>((point.Images ?? []).Select(e => new ImageViewModel(e)));
Equipment = new ObservableCollection<EquipmentViewModel>((point.Equipment ?? []).Select(e => new EquipmentViewModel(e)));
Properties = new ObservableCollection<PropertyViewModel>((point.Properties ?? []).Select(e => new PropertyViewModel(e)));
Images = new ObservableCollection<ImageViewModel>(point.Images.Select(e => new ImageViewModel(e)));
Equipment = new ObservableCollection<EquipmentViewModel>(point.Equipment.Select(e => new EquipmentViewModel(e)));
Properties = new ObservableCollection<PropertyViewModel>(point.Properties.Select(e => new PropertyViewModel(e)));
IsImagesEmpty = Images.Count == 0;
IsEquipmentEmpty = Equipment.Count == 0;
+114 -14
View File
@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Avalonia.Controls;
@@ -11,37 +13,62 @@ using CommunityToolkit.Mvvm.Messaging;
using GazpromApp.Messages;
using GazpromApp.Models;
using GazpromApp.Utils;
using Microsoft.EntityFrameworkCore;
namespace GazpromApp.ViewModels;
public partial class MainWindowViewModel : ViewModelBase
{
public ObservableCollection<PointViewModel> Points { get; }
public ObservableCollection<PointViewModel> Points { get; set; }
[RelayCommand]
private async Task AddPoint()
{
var point = await WeakReferenceMessenger.Default.Send(new NewPointMessage());
if (point == null) return;
if (point != null)
Points.Add(point);
IsLoading = true;
IsLineVisible = Points.Count > 0;
_context.Point.Add(point.GetPoint());
await _context.SaveChangesAsync();
Points.Add(point);
IsLoading = false;
}
[RelayCommand]
private async Task EditPoint(PointViewModel point)
{
var p = await WeakReferenceMessenger.Default.Send(new EditPointMessage(point)) ?? point;
Points[Points.IndexOf(point)] = p;
var p = await WeakReferenceMessenger.Default.Send(new EditPointMessage(point));
if (p == null) return;
IsLoading = true;
_context.Update(p.GetPoint());
await _context.SaveChangesAsync();
var index = Points.IndexOf(point);
if (index != -1)
Points[index] = p;
IsLoading = false;
}
[RelayCommand]
private void RemovePoint(PointViewModel point)
private async Task RemovePoint(PointViewModel point)
{
IsLoading = true;
var pointInDb = await _context.Point.FindAsync(point.GetPoint().Id);
if (pointInDb == null) return;
_context.Remove(pointInDb);
await _context.SaveChangesAsync();
Points.Remove(point);
IsLineVisible = Points.Count > 0;
IsLoading = false;
}
[RelayCommand]
@@ -54,14 +81,26 @@ public partial class MainWindowViewModel : ViewModelBase
await w.StorageProvider.TryGetFolderFromPathAsync(
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments))
});
IsLoading = true;
foreach (var file in files)
{
var url = file.Path.AbsolutePath;
var decoded = HttpUtility.HtmlDecode(url);
var point = DataSerialization.LoadPoint(decoded);
point?.SetPropertyParents();
Points.Add(new PointViewModel(point ?? new Point()));
if (point == null) continue;
_context.Point.Add(point);
await _context.SaveChangesAsync();
_context.Update(point);
await _context.SaveChangesAsync();
Points.Add(new PointViewModel(point));
}
IsLoading = false;
}
[RelayCommand]
@@ -82,14 +121,75 @@ public partial class MainWindowViewModel : ViewModelBase
}
// Visual and other properties
[ObservableProperty] private bool _isLoading = true;
[ObservableProperty]
private bool _isLineVisible;
private readonly AppDbContext _context;
private async Task LoadChildrenForProperties(List<Property> properties)
{
if (properties.Count == 0)
return;
var parentIds = properties.Select(p => p.Id).ToList();
var children = await _context.Property
.Where(p => p.ParentPropertyId.HasValue && parentIds.Contains(p.ParentPropertyId.Value))
.AsNoTracking()
.ToListAsync();
if (children.Count != 0)
{
var childrenLookup = children.ToLookup(child => child.ParentPropertyId);
foreach (var parent in properties.Where(parent => childrenLookup.Contains(parent.Id)))
parent.Properties.AddRange(childrenLookup[parent.Id]);
// ReSharper disable once TailRecursiveCall
await LoadChildrenForProperties(children);
}
}
private async Task LoadDataAsync()
{
IsLoading = true;
var pointsFromDb = await _context.Point
.Include(p => p.Images)
.Include(p => p.Equipment)
.ThenInclude(e => e.Properties)
.Include(p => p.Equipment)
.ThenInclude(e => e.Images)
.Include(p => p.Equipment)
.ThenInclude(e => e.Inspections)
.Include(p => p.Properties)
.AsNoTracking()
.ToListAsync();
var topLevelProperties = pointsFromDb.SelectMany(p => p.Properties)
.Concat(pointsFromDb.SelectMany(p => p.Equipment).SelectMany(e => e.Properties))
.ToList();
if (topLevelProperties.Count != 0)
await LoadChildrenForProperties(topLevelProperties);
Points.Clear();
foreach (var point in pointsFromDb)
Points.Add(new PointViewModel(point));
IsLoading = false;
}
public MainWindowViewModel(AppDbContext context)
{
Points = [];
_context = context;
_ = LoadDataAsync();
}
public MainWindowViewModel()
{
if (Design.IsDesignMode)
Points = [new PointViewModel(new Point()) { Name = "Name test", Description = "Description test" }];
else Points = [];
Points = [new PointViewModel(new Point()) { Name = "Name test", Description = "Description test" }];
_context = new AppDbContext();
IsLoading = false;
}
}
+26 -24
View File
@@ -1,4 +1,6 @@
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Web;
using Avalonia.Controls;
@@ -24,44 +26,44 @@ public partial class NewEquipmentDialogViewModel : ViewModelBase
[NotifyCanExecuteChangedFor(nameof(EnterEquipmentCommand))]
private string? _description;
[ObservableProperty]
private ImageViewModel? _image;
public ObservableCollection<ImageViewModel> Images { get; } = [];
[RelayCommand]
private async Task AddImage(Window w)
{
var files = await w.StorageProvider.OpenFilePickerAsync(new FilePickerOpenOptions
{
AllowMultiple = false,
Title = "Выберите картинку",
AllowMultiple = true,
Title = "Выберите одну или несколько картинок",
FileTypeFilter = [FilePickerFileTypes.ImageAll]
});
IsImageSelected = files.Count > 0;
try
foreach (var file in files)
{
Image = IsImageSelected ? new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(files[0].Path.AbsolutePath)) } : null;
}
catch (ArgumentException)
{
var error = new Error
try
{
Title = "Ошибка обработки картинки",
Message = "Картинка не была добавлена",
Severity = ErrorSeverity.Error
};
_ = await WeakReferenceMessenger.Default.Send(new ErrorMessage(new ErrorViewModel(error)));
IsImageSelected = false;
Images.Add(new ImageViewModel(new Image()) { FileName = new Bitmap(HttpUtility.UrlDecode(file.Path.AbsolutePath)) });
}
catch (ArgumentException)
{
var error = new Error
{
Title = "Ошибка обработки картинки",
Message = $"Картинка не была добавлена: \"{HttpUtility.UrlDecode(file.Name)}\"",
Severity = ErrorSeverity.Error
};
_ = await WeakReferenceMessenger.Default.Send(new ErrorMessage(new ErrorViewModel(error)));
}
}
IsImagesEmpty = Images.Count == 0;
}
[RelayCommand]
private void DeleteImage()
private void DeleteImage(ImageViewModel image)
{
Image = null;
IsImageSelected = false;
Images.Remove(image);
IsImagesEmpty = Images.Count == 0;
}
private bool CanEnterEquipment() => !string.IsNullOrEmpty(Name);
@@ -73,7 +75,7 @@ public partial class NewEquipmentDialogViewModel : ViewModelBase
{
Name = Name,
Description = Description ?? string.Empty,
Image = (Image ?? new ImageViewModel()).GetImage()
Images = Images.Select(e => e.GetImage()).ToList()
};
WeakReferenceMessenger.Default.Send(new NewEquipmentDone(new EquipmentViewModel(equipment)));
@@ -88,5 +90,5 @@ public partial class NewEquipmentDialogViewModel : ViewModelBase
// Visual and other stuff
[ObservableProperty]
private bool _isImageSelected;
private bool _isImagesEmpty;
}