diff --git a/SQLVision/Models/ITreeNode.cs b/SQLVision/Models/ITreeNode.cs
new file mode 100644
index 0000000..2826982
--- /dev/null
+++ b/SQLVision/Models/ITreeNode.cs
@@ -0,0 +1,8 @@
+namespace SQLVision.Models
+{
+ public interface ITreeNode
+ {
+ bool IsFolder { get; }
+ string Name { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/SQLVision/Models/ScriptTreeNode.cs b/SQLVision/Models/ScriptTreeNode.cs
index 0d7af09..cd3a8b0 100644
--- a/SQLVision/Models/ScriptTreeNode.cs
+++ b/SQLVision/Models/ScriptTreeNode.cs
@@ -2,7 +2,7 @@
namespace SQLVision.Models;
-public sealed class ScriptTreeNode
+public sealed class ScriptTreeNode : ITreeNode
{
public string Name { get; set; } = "";
public string? FilePath { get; set; }
diff --git a/SQLVision/SQLVision.csproj b/SQLVision/SQLVision.csproj
index ec026ea..a669874 100644
--- a/SQLVision/SQLVision.csproj
+++ b/SQLVision/SQLVision.csproj
@@ -17,6 +17,7 @@
+
@@ -48,6 +49,9 @@
Always
+
+ MSBuild:Compile
+
MSBuild:Compile
diff --git a/SQLVision/Selectors/TreeItemTemplateSelector.cs b/SQLVision/Selectors/TreeItemTemplateSelector.cs
new file mode 100644
index 0000000..382ef25
--- /dev/null
+++ b/SQLVision/Selectors/TreeItemTemplateSelector.cs
@@ -0,0 +1,18 @@
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using SQLVision.Models;
+
+namespace SQLVision.Selectors;
+
+class TreeItemTemplateSelector : DataTemplateSelector
+{
+ public DataTemplate FolderTemplate { get; set; }
+
+ public DataTemplate FileTemplate { get; set; }
+
+ protected override DataTemplate SelectTemplateCore(object item)
+ {
+ var explorerItem = (ITreeNode)item;
+ return explorerItem.IsFolder ? FolderTemplate : FileTemplate;
+ }
+}
\ No newline at end of file
diff --git a/SQLVision/Services/SqlExecutor.cs b/SQLVision/Services/SqlExecutor.cs
index 8f5d9c7..6ed7b22 100644
--- a/SQLVision/Services/SqlExecutor.cs
+++ b/SQLVision/Services/SqlExecutor.cs
@@ -10,12 +10,7 @@ namespace SQLVision.Services;
public sealed class SqlExecutor
{
- private string _connectionString;
-
- public SqlExecutor(string connectionString)
- {
- _connectionString = connectionString;
- }
+ private string _connectionString = "";
public void SetConnect(string connectionString)
{
diff --git a/SQLVision/Services/TableDataProvider.cs b/SQLVision/Services/TableDataProvider.cs
index 58b5a7b..e76e83b 100644
--- a/SQLVision/Services/TableDataProvider.cs
+++ b/SQLVision/Services/TableDataProvider.cs
@@ -7,9 +7,9 @@ namespace SQLVision.Services;
public sealed class TableDataProvider
{
- private readonly string _connectionString;
+ private string _connectionString = "";
- public TableDataProvider(string connectionString)
+ public void SetConnect(string connectionString)
{
_connectionString = connectionString;
}
diff --git a/SQLVision/ViewModels/ParameterViewModel.cs b/SQLVision/ViewModels/ParameterViewModel.cs
index ae6a11b..cfba19d 100644
--- a/SQLVision/ViewModels/ParameterViewModel.cs
+++ b/SQLVision/ViewModels/ParameterViewModel.cs
@@ -41,6 +41,16 @@ public sealed class ParameterViewModel : BaseViewModel
public ParameterViewModel(ParameterDefinition definition)
{
Definition = definition;
- Value = definition.DefaultValue;
+
+ if (definition.DefaultValue is not null)
+ {
+ Value = definition.Type switch
+ {
+ ParameterType.Bool => definition.DefaultValue.Equals("true", System.StringComparison.OrdinalIgnoreCase) || definition.DefaultValue.Equals("1", System.StringComparison.OrdinalIgnoreCase),
+ ParameterType.Int => int.TryParse(definition.DefaultValue, out var i) ? i : null,
+ ParameterType.String => definition.DefaultValue,
+ _ => definition.DefaultValue,
+ };
+ }
}
}
diff --git a/SQLVision/ViewModels/ScriptsViewModel.cs b/SQLVision/ViewModels/ScriptsViewModel.cs
new file mode 100644
index 0000000..ae859d5
--- /dev/null
+++ b/SQLVision/ViewModels/ScriptsViewModel.cs
@@ -0,0 +1,328 @@
+using Microsoft.UI.Xaml;
+using SQLVision.Models;
+using SQLVision.Services;
+using SQLVision.Views;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.IO;
+using System.Linq;
+using System.Text.Json;
+using System.Threading.Tasks;
+using System.Windows.Input;
+
+namespace SQLVision.ViewModels;
+
+public sealed partial class ScriptsViewModel : BaseViewModel
+{
+ private readonly SqlCommentParser _parser;
+ private readonly SqlExecutor _executor;
+ private readonly ParameterResolver _parameterResolver;
+ private readonly TableDataProvider _tableDataProvider;
+ private readonly ExcelExportService _excelExportService;
+
+ // -----------------------------
+ // Подключения
+ // -----------------------------
+ private readonly ConnectionStorageService _storage;
+
+ public ObservableCollection SavedConnections { get; } = new();
+
+ private ConnectionProfile? _selectedConnection;
+ public ConnectionProfile? SelectedConnection
+ {
+ get => _selectedConnection;
+ set
+ {
+ if (SetProperty(ref _selectedConnection, value))
+ {
+ if (value != null)
+ ConnectionString = value.ConnectionString;
+ }
+ }
+ }
+
+ private string _connectionString = "";
+ public string ConnectionString
+ {
+ get => _connectionString;
+ set => SetProperty(ref _connectionString, value);
+ }
+
+ // -----------------------------
+ // Скрипты, дерево и параметры
+ // -----------------------------
+ public ObservableCollection Scripts { get; } = new();
+
+ // Дерево скриптов для TreeView
+ public ObservableCollection ScriptTree { get; } = new();
+
+ private ScriptTreeNode? _selectedScriptNode;
+ public ScriptTreeNode? SelectedScriptNode
+ {
+ get => _selectedScriptNode;
+ set
+ {
+ if (SetProperty(ref _selectedScriptNode, value))
+ {
+ if (value?.FilePath is not null)
+ {
+ // Находим ScriptMetadata по пути файла
+ var meta = Scripts.FirstOrDefault(s =>
+ string.Equals(s.FilePath, value.FilePath, StringComparison.OrdinalIgnoreCase));
+
+ if (meta is not null)
+ SelectedScript = meta;
+ }
+ }
+ }
+ }
+
+ public ObservableCollection Parameters { get; } = new();
+ public ObservableCollection Outputs { get; } = new();
+
+ private ScriptMetadata? _selectedScript;
+ public ScriptMetadata? SelectedScript
+ {
+ get => _selectedScript;
+ set
+ {
+ if (SetProperty(ref _selectedScript, value))
+ {
+ (ExecuteCommand as RelayCommand)?.RaiseCanExecuteChanged();
+ _ = LoadParametersForSelectedScriptAsync();
+ }
+ }
+ }
+
+ private ExecutionResult? _lastResult;
+ public ExecutionResult? LastResult
+ {
+ get => _lastResult;
+ set
+ {
+ if (SetProperty(ref _lastResult, value))
+ {
+ (ExportToExcelCommand as RelayCommand)?.RaiseCanExecuteChanged();
+ _ = LoadParametersForSelectedScriptAsync();
+ }
+ }
+ }
+
+ // -----------------------------
+ // Команды
+ // -----------------------------
+ public ICommand ExecuteCommand { get; }
+ public ICommand ExportToExcelCommand { get; }
+ public ICommand OpenConnectionCommand { get; }
+
+ // -----------------------------
+ // Конструктор
+ // -----------------------------
+ public ScriptsViewModel(
+ SqlCommentParser parser,
+ SqlExecutor executor,
+ ParameterResolver parameterResolver,
+ TableDataProvider tableDataProvider,
+ ExcelExportService excelExportService)
+ {
+ _parser = parser;
+ _executor = executor;
+ _parameterResolver = parameterResolver;
+ _tableDataProvider = tableDataProvider;
+ _excelExportService = excelExportService;
+
+ ExecuteCommand = new RelayCommand(async _ => await ExecuteAsync(), _ => SelectedScript is not null);
+ ExportToExcelCommand = new RelayCommand(_ => ExportToExcel(), _ => LastResult is not null);
+ OpenConnectionCommand = new RelayCommand(_ => OpenConnection());
+
+ _storage = new ConnectionStorageService();
+
+ foreach (var p in _storage.Load())
+ SavedConnections.Add(p);
+
+ SelectedConnection = SavedConnections.FirstOrDefault();
+ }
+
+ // -----------------------------
+ // Окно подключения
+ // -----------------------------
+ private void OpenConnection()
+ {
+ var vm = new ConnectionViewModel();
+ var page = new ConnectionPage { DataContext = vm };
+
+ vm.ConnectionSaved += profile =>
+ {
+ var existing = SavedConnections.FirstOrDefault(p => p.Name == profile.Name);
+ if (existing is not null)
+ SavedConnections.Remove(existing);
+
+ SavedConnections.Add(profile);
+ _storage.Save(SavedConnections.ToList());
+ SelectedConnection = profile;
+ };
+
+ var window = new Window
+ {
+ Content = page
+ };
+ window.Activate();
+ }
+
+ // -----------------------------
+ // Загрузка скриптов и построение дерева
+ // -----------------------------
+ public void LoadScripts(string scriptsFolder)
+ {
+ Scripts.Clear();
+ ScriptTree.Clear();
+
+ if (!Directory.Exists(scriptsFolder))
+ return;
+
+ // Строим дерево по корневой папке
+ var rootNode = BuildScriptTree(scriptsFolder);
+ ScriptTree.Add(rootNode);
+ }
+
+ private ScriptTreeNode BuildScriptTree(string folderPath)
+ {
+ var folderNode = new ScriptTreeNode
+ {
+ Name = Path.GetFileName(folderPath),
+ FilePath = null
+ };
+
+ // Подпапки
+ foreach (var dir in Directory.GetDirectories(folderPath))
+ {
+ var subNode = BuildScriptTree(dir);
+ folderNode.Children.Add(subNode);
+ }
+
+ // Файлы .sql
+ foreach (var file in Directory.GetFiles(folderPath, "*.sql"))
+ {
+ var meta = _parser.Parse(file);
+ Scripts.Add(meta);
+
+ var fileNode = new ScriptTreeNode
+ {
+ Name = Path.GetFileName(file),
+ FilePath = file
+ };
+
+ folderNode.Children.Add(fileNode);
+ }
+
+ return folderNode;
+ }
+
+ // -----------------------------
+ // Параметры
+ // -----------------------------
+ private async Task LoadParametersForSelectedScriptAsync()
+ {
+ Parameters.Clear();
+ Outputs.Clear();
+
+ if (SelectedScript is null)
+ return;
+
+ foreach (var p in SelectedScript.Parameters)
+ {
+ var vm = new ParameterViewModel(p);
+ Parameters.Add(vm);
+
+ if (p.TableQuery is not null)
+ {
+ var table = await _tableDataProvider.LoadTableAsync(p.TableQuery);
+ vm.LookupTable = table;
+ }
+ }
+
+ foreach (var o in SelectedScript.Outputs)
+ Outputs.Add(new OutputViewModel(o));
+ }
+
+ // -----------------------------
+ // Выполнение SQL
+ // -----------------------------
+ private async Task ExecuteAsync()
+ {
+ if (SelectedScript is null)
+ return;
+
+ if (string.IsNullOrWhiteSpace(ConnectionString))
+ {
+ // TODO: вывести ContentDialog "Подключение не выбрано"
+ return;
+ }
+
+ var sqlText = await File.ReadAllTextAsync(SelectedScript.FilePath);
+
+ var paramValues = Parameters.ToDictionary(
+ p => p.Definition,
+ p => p.Value);
+
+ // Если SqlExecutor умеет менять строку подключения динамически — здесь можно прокинуть ConnectionString
+ _executor.SetConnect(ConnectionString);
+ var result = await _executor.ExecuteAsync(SelectedScript, sqlText, paramValues);
+
+ for (int i = 0; i < Outputs.Count; i++)
+ {
+ result.Tables[i].TableName = Outputs[i].Definition.Title;
+ Outputs[i].Table = result.Tables[i];
+ }
+
+ LastResult = result;
+
+
+ RaisePropertyChanged(nameof(Outputs));
+
+ SaveParameterValues(SelectedScript.FilePath, paramValues);
+ }
+
+ // -----------------------------
+ // Экспорт
+ // -----------------------------
+ private void ExportToExcel()
+ {
+ if (LastResult is null || LastResult.Tables.Count == 0)
+ return;
+
+ var directory = Path.Combine(AppContext.BaseDirectory, "Export");
+ Directory.CreateDirectory(directory);
+
+ var fileName = $"{LastResult.ScriptName}_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx";
+ var fullPath = Path.Combine(directory, fileName);
+
+ _excelExportService.ExportTablesToExcel(LastResult.Tables, fullPath);
+ }
+
+ // -----------------------------
+ // Сохранение параметров
+ // -----------------------------
+ private void SaveParameterValues(string scriptPath, Dictionary values)
+ {
+ var directory = Path.Combine(AppContext.BaseDirectory, "Config", "Parameters");
+ Directory.CreateDirectory(directory);
+
+ var fileName = Path.GetFileNameWithoutExtension(scriptPath) + ".json";
+ var fullPath = Path.Combine(directory, fileName);
+
+ var payload = new ParameterValueSet
+ {
+ ScriptFilePath = scriptPath,
+ Values = values.ToDictionary(t => t.Key.Name, t => t.Value),
+ };
+
+ var json = JsonSerializer.Serialize(payload, new JsonSerializerOptions
+ {
+ WriteIndented = true
+ });
+
+ File.WriteAllText(fullPath, json);
+ }
+}
diff --git a/SQLVision/Views/MainWindow.xaml b/SQLVision/Views/MainWindow.xaml
index 8ce0336..3512e5d 100644
--- a/SQLVision/Views/MainWindow.xaml
+++ b/SQLVision/Views/MainWindow.xaml
@@ -9,223 +9,46 @@
-
+
-
-
+
-
-
+
+
+
-
-
-
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SQLVision/Views/MainWindow.xaml.cs b/SQLVision/Views/MainWindow.xaml.cs
index 32a20de..d74d368 100644
--- a/SQLVision/Views/MainWindow.xaml.cs
+++ b/SQLVision/Views/MainWindow.xaml.cs
@@ -1,59 +1,44 @@
using Microsoft.UI.Xaml;
-using SQLVision.Services;
-using SQLVision.ViewModels;
+using SQLVision.Views.Scripts;
using System;
-using System.Data;
-using System.IO;
namespace SQLVision.Views;
public sealed partial class MainWindow : Window
{
- public MainViewModel ViewModel { get; }
public MainWindow()
{
InitializeComponent(); // , x:Class
-
- const string connectionString =
- "Server=...;Database=...;Trusted_Connection=True;TrustServerCertificate=True;";
-
- var parser = new SqlCommentParser();
- var executor = new SqlExecutor(connectionString);
- var paramResolver = new ParameterResolver();
- var tableDataProvider = new TableDataProvider(connectionString);
- var excelExport = new ExcelExportService();
-
- ViewModel = new MainViewModel(parser, executor, paramResolver, tableDataProvider, excelExport);
-
- // WinUI 3 DataContext Window Grid
- RootGrid.DataContext = ViewModel;
-
- var scriptsFolder = Path.Combine(AppContext.BaseDirectory, "Scripts");
- ViewModel.LoadScripts(scriptsFolder);
}
- private void DataGrid_Loaded(object sender, RoutedEventArgs e)
+ private void nvMain_SelectionChanged(Microsoft.UI.Xaml.Controls.NavigationView sender, Microsoft.UI.Xaml.Controls.NavigationViewSelectionChangedEventArgs args)
{
- if (sender is CommunityToolkit.WinUI.UI.Controls.DataGrid grid)
+ if (args.IsSettingsSelected)
{
- var x = grid.DataContext;
- if (grid.ItemsSource is DataView dv && dv.Table is not null)
+ //contentFrame.Navigate(typeof(SampleSettingsPage));
+ }
+ else
+ {
+ var selectedItem = (Microsoft.UI.Xaml.Controls.NavigationViewItem)args.SelectedItem;
+ if (selectedItem != null)
{
- foreach (System.Data.DataColumn column in dv.Table.Columns)
+ string selectedItemTag = ((string)selectedItem.Tag);
+ Type? page = selectedItemTag switch
{
- grid.Columns.Add(new CommunityToolkit.WinUI.UI.Controls.DataGridTextColumn
- {
- Header = column.ColumnName,
- // DataRowView
- Binding = new Microsoft.UI.Xaml.Data.Binding
- {
- Path = new Microsoft.UI.Xaml.PropertyPath($"[{column.ColumnName}]")
- }
- });
- }
+ "ScriptsTag" => typeof(ScriptsPage),
+ _ => null,
+ };
+
+ if (page is null) return;
+
+ contentFrame.Navigate(page);
}
}
}
+ private void TitleBar_PaneToggleRequested(Microsoft.UI.Xaml.Controls.TitleBar sender, object args)
+ {
+ nvMain.IsPaneOpen = !nvMain.IsPaneOpen;
+ }
}
diff --git a/SQLVision/Views/Scripts/ScriptsPage.xaml b/SQLVision/Views/Scripts/ScriptsPage.xaml
new file mode 100644
index 0000000..25a7921
--- /dev/null
+++ b/SQLVision/Views/Scripts/ScriptsPage.xaml
@@ -0,0 +1,218 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SQLVision/Views/Scripts/ScriptsPage.xaml.cs b/SQLVision/Views/Scripts/ScriptsPage.xaml.cs
new file mode 100644
index 0000000..49c5f7d
--- /dev/null
+++ b/SQLVision/Views/Scripts/ScriptsPage.xaml.cs
@@ -0,0 +1,54 @@
+using Microsoft.UI.Xaml;
+using Microsoft.UI.Xaml.Controls;
+using SQLVision.Services;
+using SQLVision.ViewModels;
+using System;
+using System.Data;
+using System.IO;
+
+namespace SQLVision.Views.Scripts;
+
+public sealed partial class ScriptsPage : Page
+{
+ public ScriptsViewModel ViewModel { get; }
+
+ public ScriptsPage()
+ {
+ InitializeComponent();
+
+ var parser = new SqlCommentParser();
+ var executor = new SqlExecutor();
+ var paramResolver = new ParameterResolver();
+ var tableDataProvider = new TableDataProvider();
+ var excelExport = new ExcelExportService();
+
+ ViewModel = new ScriptsViewModel(parser, executor, paramResolver, tableDataProvider, excelExport);
+
+ var scriptsFolder = Path.Combine(AppContext.BaseDirectory, "Scripts");
+ ViewModel.LoadScripts(scriptsFolder);
+ this.DataContext = this;
+ }
+
+ private void DataGrid_Loaded(object sender, RoutedEventArgs e)
+ {
+ if (sender is CommunityToolkit.WinUI.UI.Controls.DataGrid grid)
+ {
+ var x = grid.DataContext;
+ if (grid.ItemsSource is DataView dv && dv.Table is not null)
+ {
+ foreach (System.Data.DataColumn column in dv.Table.Columns)
+ {
+ grid.Columns.Add(new CommunityToolkit.WinUI.UI.Controls.DataGridTextColumn
+ {
+ Header = column.ColumnName,
+ // DataRowView
+ Binding = new Microsoft.UI.Xaml.Data.Binding
+ {
+ Path = new Microsoft.UI.Xaml.PropertyPath($"[{column.ColumnName}]")
+ }
+ });
+ }
+ }
+ }
+ }
+}