edit
This commit is contained in:
@@ -53,9 +53,6 @@
|
||||
VerticalAlignment="Center"
|
||||
KeyDown="SearchTextBox_KeyDown"
|
||||
Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<Button Margin="0,0,8,0"
|
||||
Click="RefreshButton_Click"
|
||||
Content="Обновить список" />
|
||||
<TextBlock Margin="12,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
FontWeight="SemiBold"
|
||||
@@ -94,7 +91,7 @@
|
||||
|
||||
<GroupBox Grid.Column="0" Header="Реестр средств измерений">
|
||||
<Grid Margin="8">
|
||||
<DataGrid x:Name="InstrumentGrid"
|
||||
<DataGrid x:Name="InstrumentGrid" AutoGenerateColumns="False"
|
||||
ItemsSource="{Binding Instruments}"
|
||||
SelectedItem="{Binding SelectedSummary, Mode=TwoWay}"
|
||||
IsReadOnly="True"
|
||||
@@ -241,7 +238,7 @@
|
||||
Content="Удалить привязку" />
|
||||
</StackPanel>
|
||||
|
||||
<DataGrid x:Name="AttachmentGrid"
|
||||
<DataGrid x:Name="AttachmentGrid" AutoGenerateColumns="False"
|
||||
Grid.Row="1"
|
||||
ItemsSource="{Binding SelectedInstrument.Attachments}"
|
||||
IsReadOnly="True">
|
||||
|
||||
@@ -29,11 +29,6 @@ public partial class MainWindow : Window
|
||||
await ExecuteUiAsync(_viewModel.InitializeAsync);
|
||||
}
|
||||
|
||||
private async void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await ExecuteUiAsync(() => _viewModel.RefreshAsync());
|
||||
}
|
||||
|
||||
private async void SyncButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
await ExecuteUiAsync(async () =>
|
||||
|
||||
@@ -15,7 +15,15 @@ internal sealed class PdfStorageService
|
||||
_client = client;
|
||||
var options = configuration.GetSection("Crawler").Get<CrawlerOptions>()
|
||||
?? throw new InvalidOperationException("Раздел Crawler не найден в appsettings.json.");
|
||||
_rootPath = Environment.ExpandEnvironmentVariables(options.PdfStoragePath);
|
||||
var configuredPath = Environment.ExpandEnvironmentVariables(options.PdfStoragePath ?? string.Empty).Trim();
|
||||
if (string.IsNullOrWhiteSpace(configuredPath))
|
||||
{
|
||||
configuredPath = "PdfStore";
|
||||
}
|
||||
|
||||
_rootPath = Path.IsPathRooted(configuredPath)
|
||||
? configuredPath
|
||||
: Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, configuredPath));
|
||||
Directory.CreateDirectory(_rootPath);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
@@ -11,6 +12,8 @@ namespace CRAWLER.ViewModels;
|
||||
|
||||
internal sealed class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
private const int SearchRefreshDelayMilliseconds = 350;
|
||||
|
||||
private readonly InstrumentCatalogService _catalogService;
|
||||
private readonly IPdfOpener _pdfOpener;
|
||||
private InstrumentSummary _selectedSummary;
|
||||
@@ -19,6 +22,8 @@ internal sealed class MainWindowViewModel : ObservableObject
|
||||
private int _pagesToScan;
|
||||
private string _statusText;
|
||||
private bool _isBusy;
|
||||
private bool _isInitialized;
|
||||
private CancellationTokenSource _searchRefreshCancellationTokenSource;
|
||||
private CancellationTokenSource _selectionCancellationTokenSource;
|
||||
|
||||
public MainWindowViewModel(InstrumentCatalogService catalogService, IPdfOpener pdfOpener)
|
||||
@@ -53,7 +58,13 @@ internal sealed class MainWindowViewModel : ObservableObject
|
||||
public string SearchText
|
||||
{
|
||||
get { return _searchText; }
|
||||
set { SetProperty(ref _searchText, value); }
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _searchText, value) && _isInitialized)
|
||||
{
|
||||
ScheduleSearchRefresh();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int PagesToScan
|
||||
@@ -80,40 +91,14 @@ internal sealed class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
StatusText = "Подготовка базы данных...";
|
||||
await _catalogService.InitializeAsync(CancellationToken.None);
|
||||
await RefreshAsync();
|
||||
await RefreshCoreAsync();
|
||||
_isInitialized = true;
|
||||
});
|
||||
}
|
||||
|
||||
public async Task RefreshAsync(long? selectId = null)
|
||||
{
|
||||
await RunBusyAsync(async () =>
|
||||
{
|
||||
StatusText = "Загрузка списка записей...";
|
||||
|
||||
var items = await _catalogService.SearchAsync(SearchText, CancellationToken.None);
|
||||
Instruments.Clear();
|
||||
foreach (var item in items)
|
||||
{
|
||||
Instruments.Add(item);
|
||||
}
|
||||
|
||||
if (Instruments.Count == 0)
|
||||
{
|
||||
SelectedInstrument = null;
|
||||
SelectedSummary = null;
|
||||
StatusText = "Записи не найдены.";
|
||||
return;
|
||||
}
|
||||
|
||||
var summary = selectId.HasValue
|
||||
? Instruments.FirstOrDefault(item => item.Id == selectId.Value)
|
||||
: SelectedSummary == null
|
||||
? Instruments.FirstOrDefault()
|
||||
: Instruments.FirstOrDefault(item => item.Id == SelectedSummary.Id) ?? Instruments.FirstOrDefault();
|
||||
|
||||
SelectedSummary = summary;
|
||||
StatusText = $"Загружено записей: {Instruments.Count}.";
|
||||
});
|
||||
await RunBusyAsync(() => RefreshCoreAsync(selectId));
|
||||
}
|
||||
|
||||
public async Task<SyncResult> SyncAsync()
|
||||
@@ -124,7 +109,7 @@ internal sealed class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
var progress = new Progress<string>(message => StatusText = message);
|
||||
result = await _catalogService.SyncFromSiteAsync(PagesToScan, progress, CancellationToken.None);
|
||||
await RefreshAsync(SelectedSummary?.Id);
|
||||
await RefreshCoreAsync(SelectedSummary?.Id);
|
||||
});
|
||||
|
||||
return result;
|
||||
@@ -143,7 +128,7 @@ internal sealed class MainWindowViewModel : ObservableObject
|
||||
return SelectedInstrument?.Clone();
|
||||
}
|
||||
|
||||
public async Task<long> SaveAsync(InstrumentRecord draft, System.Collections.Generic.IEnumerable<string> pendingPdfPaths)
|
||||
public async Task<long> SaveAsync(InstrumentRecord draft, IEnumerable<string> pendingPdfPaths)
|
||||
{
|
||||
long id = 0;
|
||||
|
||||
@@ -151,7 +136,7 @@ internal sealed class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
StatusText = "Сохранение записи...";
|
||||
id = await _catalogService.SaveInstrumentAsync(draft, pendingPdfPaths, CancellationToken.None);
|
||||
await RefreshAsync(id);
|
||||
await RefreshCoreAsync(id);
|
||||
StatusText = "Изменения сохранены.";
|
||||
});
|
||||
|
||||
@@ -171,12 +156,12 @@ internal sealed class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
StatusText = "Удаление записи...";
|
||||
await _catalogService.DeleteInstrumentAsync(SelectedInstrument, CancellationToken.None);
|
||||
await RefreshAsync();
|
||||
await RefreshCoreAsync();
|
||||
StatusText = $"Запись {deletedId} удалена.";
|
||||
});
|
||||
}
|
||||
|
||||
public async Task AddAttachmentsToSelectedAsync(System.Collections.Generic.IEnumerable<string> paths)
|
||||
public async Task AddAttachmentsToSelectedAsync(IEnumerable<string> paths)
|
||||
{
|
||||
if (SelectedInstrument == null)
|
||||
{
|
||||
@@ -221,9 +206,66 @@ internal sealed class MainWindowViewModel : ObservableObject
|
||||
}
|
||||
}
|
||||
|
||||
private async Task RefreshCoreAsync(long? selectId = null)
|
||||
{
|
||||
StatusText = "Загрузка списка записей...";
|
||||
|
||||
var items = await _catalogService.SearchAsync(SearchText, CancellationToken.None);
|
||||
Instruments.Clear();
|
||||
foreach (var item in items)
|
||||
{
|
||||
Instruments.Add(item);
|
||||
}
|
||||
|
||||
if (Instruments.Count == 0)
|
||||
{
|
||||
SelectedInstrument = null;
|
||||
SelectedSummary = null;
|
||||
StatusText = "Записи не найдены.";
|
||||
return;
|
||||
}
|
||||
|
||||
var summary = selectId.HasValue
|
||||
? Instruments.FirstOrDefault(item => item.Id == selectId.Value)
|
||||
: SelectedSummary == null
|
||||
? Instruments.FirstOrDefault()
|
||||
: Instruments.FirstOrDefault(item => item.Id == SelectedSummary.Id) ?? Instruments.FirstOrDefault();
|
||||
|
||||
SelectedSummary = summary;
|
||||
StatusText = $"Загружено записей: {Instruments.Count}.";
|
||||
}
|
||||
|
||||
private void ScheduleSearchRefresh()
|
||||
{
|
||||
_searchRefreshCancellationTokenSource?.Cancel();
|
||||
_searchRefreshCancellationTokenSource?.Dispose();
|
||||
_searchRefreshCancellationTokenSource = new CancellationTokenSource();
|
||||
_ = RunSearchRefreshAsync(_searchRefreshCancellationTokenSource.Token);
|
||||
}
|
||||
|
||||
private async Task RunSearchRefreshAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await Task.Delay(SearchRefreshDelayMilliseconds, cancellationToken);
|
||||
|
||||
while (IsBusy)
|
||||
{
|
||||
await Task.Delay(100, cancellationToken);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
await RefreshAsync();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadSelectedInstrumentAsync(long? id)
|
||||
{
|
||||
_selectionCancellationTokenSource?.Cancel();
|
||||
_selectionCancellationTokenSource?.Dispose();
|
||||
_selectionCancellationTokenSource = new CancellationTokenSource();
|
||||
var token = _selectionCancellationTokenSource.Token;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
"CatalogPathFormat": "/poverka/gosreestr_sredstv_izmereniy?page={0}",
|
||||
"RequestDelayMilliseconds": 350,
|
||||
"DefaultPagesToScan": 1,
|
||||
"PdfStoragePath": "%LOCALAPPDATA%\\CRAWLER\\PdfStore",
|
||||
"PdfStoragePath": "PdfStore",
|
||||
"TimeoutSeconds": 30,
|
||||
"UserAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) CRAWLER/1.0"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user