/
d4nil
/
Konenkov_2413_software_engineering_
Обзор
Документация
Войти
/
d4nil
/
Konenkov_2413_software_engineering_
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
Interface/ViewModels/EditEngine/EditEngineViewModelBase.cs
189 строк
6 KB
Konenkov Daniel
feat: изменить поведение окна создания и редактирования двигателя при ошибке на сервере - теперь оно не зыкрывается
23 дек 2025, 20:38
23 дек 2025, 20:38
c43ab00
Код
Авторство
О чём код?
using System; using System.Reactive; using System.Threading.Tasks; using Engines.Engines; using Interface.Models; using Interface.Services.Api; using ReactiveUI; namespace Interface.ViewModels.EditEngine; /// <summary> /// Базовый VM для диалогов редактирования двигателя с общей валидацией. /// </summary> public abstract class EditEngineViewModelBase<T> : ViewModelBase where T : Engine { private readonly EditMode _mode; /// <summary> /// Текст ошибки валидации для UI. /// </summary> private string _validationMessage = string.Empty; /// <summary> /// Флаг наличия ошибки валидации. /// </summary> private bool _hasValidationMessage; /// <summary> /// Базовый конструктор с клонированием двигателя. /// </summary> /// <param name="engine">Двигатель для редактирования.</param> /// <param name="mode">Режим работы диалога.</param> protected EditEngineViewModelBase(T? engine, EditMode mode) { Editable = (T?)engine?.Clone() ?? CreateDefault(); _mode = mode; Title = BuildTitle(mode); ConfirmCommand = ReactiveCommand.CreateFromTask(ValidateAndReturnAsync); } /// <summary> /// Редактируемый двигатель. /// </summary> public T Editable { get; } /// <summary> /// Флаг доступности полей для редактирования. /// </summary> public bool IsReadOnly => _mode == EditMode.Delete; /// <summary> /// Показывать ли поле ID. /// </summary> public bool ShowId => _mode != EditMode.Create && _mode != EditMode.InitializePrototype; /// <summary> /// Заголовок окна. /// </summary> public string Title { get; } /// <summary> /// Команда подтверждения. /// </summary> public ReactiveCommand<Unit, T?> ConfirmCommand { get; } /// <summary> /// Сообщение о валидации для отображения в диалоге. /// </summary> public string ValidationMessage { get => _validationMessage; private set { this.RaiseAndSetIfChanged(ref _validationMessage, value); HasValidationMessage = !string.IsNullOrWhiteSpace(value); } } /// <summary> /// Флаг наличия сообщения валидации (для скрытия/показа статуса). /// </summary> public bool HasValidationMessage { get => _hasValidationMessage; private set => this.RaiseAndSetIfChanged(ref _hasValidationMessage, value); } /// <summary> /// Можно ли закрывать окно после подтверждения. /// </summary> public bool CanClose { get => _canClose; private set => this.RaiseAndSetIfChanged(ref _canClose, value); } private bool _canClose; /// <summary> /// Создаёт экземпляр двигателя по умолчанию. /// </summary> /// <returns>Экземпляр типа <typeparamref name="T"/>.</returns> protected abstract T CreateDefault(); /// <summary> /// Формирует заголовок окна редактирования. /// </summary> /// <param name="mode">Текущий режим.</param> /// <returns>Заголовок окна.</returns> protected abstract string BuildTitle(EditMode mode); /// <summary> /// Дополнительный обработчик отправки на сервер для режима создания. /// </summary> public Func<T, Task<ApiResult<Engine>>>? SubmitAsync { get; set; } /// <summary> /// Коллбек для вывода статуса в главное окно. /// </summary> public Action<string>? ReportStatus { get; set; } /// <summary> /// Разрешает ли пользователь отправку невалидного запроса. /// </summary> public bool AllowInvalidRequests { get; set; } /// <summary> /// Выполняет валидацию и возвращает редактируемый объект. /// </summary> /// <returns>Валидный двигатель или <c>null</c> при ошибке.</returns> private async Task<T?> ValidateAndReturnAsync() { CanClose = false; if (_mode == EditMode.Create) { return await SubmitToServerAsync("Ошибка сервера при создании"); } var errors = EngineValidator.Validate(Editable, requireId: ShowId); if (errors.Count > 0) { ValidationMessage = string.Join("\n", errors); if (!AllowInvalidRequests) { return null; } ReportStatus?.Invoke($"Отправляем с ошибками: {ValidationMessage.Replace('\n', ' ')}"); ValidationMessage = string.Empty; } if (SubmitAsync is null) { await Task.CompletedTask; ValidationMessage = string.Empty; CanClose = true; return Editable; } return await SubmitToServerAsync("Ошибка сервера при сохранении"); } /// <summary> /// Отправляет данные на сервер и возвращает результат при успехе. /// </summary> /// <param name="fallbackMessage">Сообщение об ошибке по умолчанию.</param> /// <returns>Результат сохранения или <c>null</c> при ошибке.</returns> private async Task<T?> SubmitToServerAsync(string fallbackMessage) { if (SubmitAsync is null) { ValidationMessage = "Отправка на сервер не настроена"; ReportStatus?.Invoke(ValidationMessage); return null; } var apiResult = await SubmitAsync(Editable); if (!apiResult.IsSuccess || apiResult.Data is null) { ValidationMessage = apiResult.Error ?? fallbackMessage; ReportStatus?.Invoke(ValidationMessage); return null; } ValidationMessage = string.Empty; CanClose = true; return apiResult.Data as T ?? Editable; } }