Добавьте файлы проекта.
This commit is contained in:
528
SQLVision.UI/ViewModels/MainViewModel.cs
Normal file
528
SQLVision.UI/ViewModels/MainViewModel.cs
Normal file
@@ -0,0 +1,528 @@
|
||||
using SQLVision.Core.Enums;
|
||||
using SQLVision.Core.Interfaces;
|
||||
using SQLVision.Core.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Data;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using Windows.ApplicationModel.DataTransfer;
|
||||
|
||||
namespace SQLVision.UI.ViewModels;
|
||||
|
||||
public class MainViewModel : ObservableObject
|
||||
{
|
||||
private readonly IScriptManager _scriptManager;
|
||||
private readonly ISqlExecutionService _executionService;
|
||||
private readonly IExportService _exportService;
|
||||
private readonly IControlFactory _controlFactory;
|
||||
private readonly IVisualizerFactory _visualizerFactory;
|
||||
private readonly ILogger<MainViewModel> _logger;
|
||||
|
||||
private readonly ObservableCollection<ScriptMetadata> _scripts = new();
|
||||
private readonly ObservableCollection<ScriptCategory> _scriptCategories = new();
|
||||
private readonly ObservableCollection<ExecutionHistoryItem> _history = new();
|
||||
private readonly ObservableCollection<ResultTabViewModel> _resultTabs = new();
|
||||
|
||||
private ScriptMetadata _selectedScript;
|
||||
private bool _isBusy;
|
||||
private string _statusMessage;
|
||||
private ResultTabViewModel _selectedResultTab;
|
||||
private string _searchText;
|
||||
|
||||
public ObservableCollection<ScriptCategory> ScriptCategories => _scriptCategories;
|
||||
public ObservableCollection<ExecutionHistoryItem> History => _history;
|
||||
public ObservableCollection<ResultTabViewModel> ResultTabs => _resultTabs;
|
||||
|
||||
public ScriptMetadata SelectedScript
|
||||
{
|
||||
get => _selectedScript;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedScript, value))
|
||||
{
|
||||
OnSelectedScriptChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsBusy
|
||||
{
|
||||
get => _isBusy;
|
||||
set => SetProperty(ref _isBusy, value);
|
||||
}
|
||||
|
||||
public string StatusMessage
|
||||
{
|
||||
get => _statusMessage;
|
||||
set => SetProperty(ref _statusMessage, value);
|
||||
}
|
||||
|
||||
public ResultTabViewModel SelectedResultTab
|
||||
{
|
||||
get => _selectedResultTab;
|
||||
set => SetProperty(ref _selectedResultTab, value);
|
||||
}
|
||||
|
||||
public string SearchText
|
||||
{
|
||||
get => _searchText;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _searchText, value))
|
||||
{
|
||||
FilterScripts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<ParameterViewModel> Parameters { get; } = new();
|
||||
|
||||
public IAsyncRelayCommand LoadScriptsCommand { get; }
|
||||
public IAsyncRelayCommand ExecuteCommand { get; }
|
||||
public IAsyncRelayCommand ExportCommand { get; }
|
||||
public IRelayCommand CopySqlCommand { get; }
|
||||
public IRelayCommand ClearResultsCommand { get; }
|
||||
public IRelayCommand SaveParametersCommand { get; }
|
||||
public IRelayCommand LoadParametersCommand { get; }
|
||||
|
||||
public MainViewModel(
|
||||
IScriptManager scriptManager,
|
||||
ISqlExecutionService executionService,
|
||||
IExportService exportService,
|
||||
IControlFactory controlFactory,
|
||||
IVisualizerFactory visualizerFactory,
|
||||
ILogger<MainViewModel> logger)
|
||||
{
|
||||
_scriptManager = scriptManager;
|
||||
_executionService = executionService;
|
||||
_exportService = exportService;
|
||||
_controlFactory = controlFactory;
|
||||
_visualizerFactory = visualizerFactory;
|
||||
_logger = logger;
|
||||
|
||||
LoadScriptsCommand = new AsyncRelayCommand(LoadScriptsAsync);
|
||||
ExecuteCommand = new AsyncRelayCommand(ExecuteScriptAsync, CanExecuteScript);
|
||||
ExportCommand = new AsyncRelayCommand(ExportResultsAsync, CanExportResults);
|
||||
CopySqlCommand = new RelayCommand(CopySqlToClipboard);
|
||||
ClearResultsCommand = new RelayCommand(ClearResults);
|
||||
SaveParametersCommand = new RelayCommand(SaveParameters);
|
||||
LoadParametersCommand = new RelayCommand(LoadParameters);
|
||||
|
||||
// Подписка на события
|
||||
_scriptManager.ScriptChanged += OnScriptChanged;
|
||||
_scriptManager.ScriptsReloaded += OnScriptsReloaded;
|
||||
}
|
||||
|
||||
private async Task LoadScriptsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
StatusMessage = "Загрузка скриптов...";
|
||||
|
||||
var scripts = await _scriptManager.LoadScriptsAsync();
|
||||
_scripts.Clear();
|
||||
|
||||
foreach (var script in scripts)
|
||||
{
|
||||
_scripts.Add(script);
|
||||
}
|
||||
|
||||
CategorizeScripts();
|
||||
StatusMessage = $"Загружено {_scripts.Count} скриптов";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error loading scripts");
|
||||
StatusMessage = $"Ошибка загрузки: {ex.Message}";
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CategorizeScripts()
|
||||
{
|
||||
_scriptCategories.Clear();
|
||||
|
||||
var categories = _scripts
|
||||
.GroupBy(s => s.Category ?? "Без категории")
|
||||
.OrderBy(g => g.Key);
|
||||
|
||||
foreach (var category in categories)
|
||||
{
|
||||
var scriptCategory = new ScriptCategory
|
||||
{
|
||||
Name = category.Key,
|
||||
Scripts = new ObservableCollection<ScriptMetadata>(category.OrderBy(s => s.FileName))
|
||||
};
|
||||
|
||||
_scriptCategories.Add(scriptCategory);
|
||||
}
|
||||
}
|
||||
|
||||
private void FilterScripts()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(SearchText))
|
||||
{
|
||||
// Показать все скрипты
|
||||
foreach (var category in _scriptCategories)
|
||||
{
|
||||
foreach (var script in category.Scripts)
|
||||
{
|
||||
script.IsVisible = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var searchLower = SearchText.ToLower();
|
||||
|
||||
foreach (var category in _scriptCategories)
|
||||
{
|
||||
foreach (var script in category.Scripts)
|
||||
{
|
||||
script.IsVisible =
|
||||
script.FileName.ToLower().Contains(searchLower) ||
|
||||
script.Description?.ToLower().Contains(searchLower) == true ||
|
||||
script.Tags.Any(t => t.ToLower().Contains(searchLower));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnSelectedScriptChanged()
|
||||
{
|
||||
Parameters.Clear();
|
||||
|
||||
if (_selectedScript == null) return;
|
||||
|
||||
// Создание ViewModel для каждого параметра
|
||||
foreach (var param in _selectedScript.Parameters.OrderBy(p => p.Order))
|
||||
{
|
||||
var paramVm = new ParameterViewModel(param, _controlFactory);
|
||||
paramVm.ValueChanged += OnParameterValueChanged;
|
||||
Parameters.Add(paramVm);
|
||||
}
|
||||
|
||||
// Восстановление сохраненных значений
|
||||
LoadSavedParameters();
|
||||
}
|
||||
|
||||
private void OnParameterValueChanged(object sender, EventArgs e)
|
||||
{
|
||||
ExecuteCommand.NotifyCanExecuteChanged();
|
||||
|
||||
// Обновление зависимых параметров
|
||||
if (sender is ParameterViewModel changedParam)
|
||||
{
|
||||
UpdateDependentParameters(changedParam);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateDependentParameters(ParameterViewModel changedParam)
|
||||
{
|
||||
foreach (var paramVm in Parameters)
|
||||
{
|
||||
if (paramVm.Parameter.DependsOn == changedParam.Parameter.Name)
|
||||
{
|
||||
paramVm.UpdateDependencies(GetCurrentParameterValues());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, object> GetCurrentParameterValues()
|
||||
{
|
||||
return Parameters.ToDictionary(
|
||||
p => p.Parameter.Name,
|
||||
p => p.Value ?? p.Parameter.DefaultValue);
|
||||
}
|
||||
|
||||
private bool CanExecuteScript()
|
||||
{
|
||||
if (_selectedScript == null) return false;
|
||||
|
||||
// Проверка обязательных параметров
|
||||
foreach (var paramVm in Parameters)
|
||||
{
|
||||
if (paramVm.Parameter.IsRequired &&
|
||||
(paramVm.Value == null || string.IsNullOrWhiteSpace(paramVm.Value.ToString())))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task ExecuteScriptAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsBusy = true;
|
||||
StatusMessage = "Выполнение скрипта...";
|
||||
|
||||
var parameters = GetCurrentParameterValues();
|
||||
|
||||
var result = await _executionService.ExecuteAsync(_selectedScript, parameters);
|
||||
|
||||
// Добавление в историю
|
||||
var historyItem = new ExecutionHistoryItem
|
||||
{
|
||||
ScriptId = _selectedScript.Id,
|
||||
ScriptName = _selectedScript.FileName,
|
||||
ExecutionTime = DateTime.Now,
|
||||
Duration = result.ExecutionTime,
|
||||
Success = result.IsSuccess,
|
||||
Parameters = new Dictionary<string, object>(parameters),
|
||||
RowCount = result.RowCount,
|
||||
ErrorMessage = result.ErrorMessage,
|
||||
ExecutedSql = result.ExecutedSql
|
||||
};
|
||||
|
||||
_history.Insert(0, historyItem);
|
||||
|
||||
// Очистка старых записей истории
|
||||
while (_history.Count > 1000)
|
||||
{
|
||||
_history.RemoveAt(_history.Count - 1);
|
||||
}
|
||||
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
CreateResultTabs(result);
|
||||
StatusMessage = $"Выполнено за {result.ExecutionTime.TotalSeconds:F2} сек. Получено строк: {result.RowCount}";
|
||||
}
|
||||
else
|
||||
{
|
||||
StatusMessage = $"Ошибка: {result.ErrorMessage}";
|
||||
ShowErrorDialog(result.ErrorMessage);
|
||||
}
|
||||
|
||||
// Сохранение параметров
|
||||
SaveParameters();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error executing script");
|
||||
StatusMessage = $"Ошибка: {ex.Message}";
|
||||
ShowErrorDialog(ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateResultTabs(ExecutionResult result)
|
||||
{
|
||||
// Удаляем старые вкладки
|
||||
ResultTabs.Clear();
|
||||
|
||||
for (int i = 0; i < result.Data.Tables.Count; i++)
|
||||
{
|
||||
var table = result.Data.Tables[i];
|
||||
var outputDef = i < _selectedScript.Outputs.Count
|
||||
? _selectedScript.Outputs[i]
|
||||
: CreateDefaultOutputDefinition(table, i);
|
||||
|
||||
var visualizer = _visualizerFactory.GetVisualizer(outputDef.Type);
|
||||
var content = visualizer.Visualize(table, outputDef);
|
||||
|
||||
var tabVm = new ResultTabViewModel
|
||||
{
|
||||
Title = outputDef.Description,
|
||||
Content = content,
|
||||
DataTable = table,
|
||||
OutputDefinition = outputDef,
|
||||
CanExport = true,
|
||||
CanCopy = true
|
||||
};
|
||||
|
||||
ResultTabs.Add(tabVm);
|
||||
}
|
||||
|
||||
if (ResultTabs.Any())
|
||||
{
|
||||
SelectedResultTab = ResultTabs[0];
|
||||
}
|
||||
}
|
||||
|
||||
private OutputDefinition CreateDefaultOutputDefinition(DataTable table, int index)
|
||||
{
|
||||
return new OutputDefinition
|
||||
{
|
||||
Type = OutputType.Table,
|
||||
Description = $"Результат {index + 1}",
|
||||
DataTableName = table.TableName
|
||||
};
|
||||
}
|
||||
|
||||
private bool CanExportResults()
|
||||
{
|
||||
return SelectedResultTab != null &&
|
||||
SelectedResultTab.DataTable != null &&
|
||||
SelectedResultTab.DataTable.Rows.Count > 0;
|
||||
}
|
||||
|
||||
private async Task ExportResultsAsync()
|
||||
{
|
||||
if (SelectedResultTab?.DataTable == null) return;
|
||||
|
||||
try
|
||||
{
|
||||
var savePicker = new FileSavePicker();
|
||||
savePicker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
|
||||
savePicker.FileTypeChoices.Add("Excel файл", new List<string> { ".xlsx" });
|
||||
savePicker.FileTypeChoices.Add("CSV файл", new List<string> { ".csv" });
|
||||
savePicker.FileTypeChoices.Add("JSON файл", new List<string> { ".json" });
|
||||
savePicker.SuggestedFileName = $"{_selectedScript.FileName}_{DateTime.Now:yyyyMMdd_HHmmss}";
|
||||
|
||||
var file = await savePicker.PickSaveFileAsync();
|
||||
if (file != null)
|
||||
{
|
||||
var options = new ExportOptions
|
||||
{
|
||||
Format = Path.GetExtension(file.Path).TrimStart('.').ToUpper(),
|
||||
IncludeHeaders = true,
|
||||
AutoFilter = true
|
||||
};
|
||||
|
||||
await _exportService.ExportAsync(SelectedResultTab.DataTable, file.Path, options);
|
||||
|
||||
StatusMessage = $"Экспортировано в {file.Path}";
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error exporting results");
|
||||
StatusMessage = $"Ошибка экспорта: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
private void CopySqlToClipboard()
|
||||
{
|
||||
if (_selectedScript == null) return;
|
||||
|
||||
var parameters = GetCurrentParameterValues();
|
||||
var sql = FormatSqlWithParameters(_selectedScript.ProcessedSql, parameters);
|
||||
|
||||
var package = new DataPackage();
|
||||
package.SetText(sql);
|
||||
Clipboard.SetContent(package);
|
||||
|
||||
StatusMessage = "SQL скопирован в буфер обмена";
|
||||
}
|
||||
|
||||
private string FormatSqlWithParameters(string sql, Dictionary<string, object> parameters)
|
||||
{
|
||||
var result = new StringBuilder(sql);
|
||||
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
var paramName = $"@{param.Key}";
|
||||
var paramValue = FormatParameterForDisplay(param.Value);
|
||||
|
||||
result = result.Replace(paramName, paramValue);
|
||||
}
|
||||
|
||||
return result.ToString();
|
||||
}
|
||||
|
||||
private string FormatParameterForDisplay(object value)
|
||||
{
|
||||
if (value == null) return "NULL";
|
||||
|
||||
return value switch
|
||||
{
|
||||
string str => $"N'{str.Replace("'", "''")}'",
|
||||
DateTime dt => $"'{dt:yyyy-MM-dd HH:mm:ss}'",
|
||||
bool b => b ? "1" : "0",
|
||||
_ => value.ToString()
|
||||
};
|
||||
}
|
||||
|
||||
private void ClearResults()
|
||||
{
|
||||
ResultTabs.Clear();
|
||||
StatusMessage = "Результаты очищены";
|
||||
}
|
||||
|
||||
private void SaveParameters()
|
||||
{
|
||||
if (_selectedScript == null) return;
|
||||
|
||||
var parameters = GetCurrentParameterValues();
|
||||
var settings = ApplicationData.Current.LocalSettings;
|
||||
|
||||
var dict = new Dictionary<string, object>();
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
dict[param.Key] = param.Value;
|
||||
}
|
||||
|
||||
var json = JsonSerializer.Serialize(dict);
|
||||
settings.Values[$"ScriptParams_{_selectedScript.Id}"] = json;
|
||||
|
||||
StatusMessage = "Параметры сохранены";
|
||||
}
|
||||
|
||||
private void LoadParameters()
|
||||
{
|
||||
if (_selectedScript == null) return;
|
||||
|
||||
var settings = ApplicationData.Current.LocalSettings;
|
||||
if (settings.Values.TryGetValue($"ScriptParams_{_selectedScript.Id}", out var jsonObj))
|
||||
{
|
||||
try
|
||||
{
|
||||
var savedParams = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonObj.ToString());
|
||||
|
||||
foreach (var paramVm in Parameters)
|
||||
{
|
||||
if (savedParams.TryGetValue(paramVm.Parameter.Name, out var value))
|
||||
{
|
||||
paramVm.Value = value;
|
||||
}
|
||||
}
|
||||
|
||||
StatusMessage = "Параметры загружены";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Error loading saved parameters");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowErrorDialog(string message)
|
||||
{
|
||||
// Реализация диалога ошибки
|
||||
// Можно использовать ContentDialog или другое UI решение
|
||||
}
|
||||
|
||||
private void OnScriptChanged(object sender, ScriptChangedEventArgs e)
|
||||
{
|
||||
// Обновление UI при изменении скрипта
|
||||
DispatcherQueue.GetForCurrentThread().TryEnqueue(() =>
|
||||
{
|
||||
CategorizeScripts();
|
||||
|
||||
if (e.ChangeType == ScriptChangeType.Deleted &&
|
||||
_selectedScript?.FullPath == e.FilePath)
|
||||
{
|
||||
SelectedScript = null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void OnScriptsReloaded(object sender, ScriptsReloadedEventArgs e)
|
||||
{
|
||||
DispatcherQueue.GetForCurrentThread().TryEnqueue(CategorizeScripts);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user