Добавьте файлы проекта.

This commit is contained in:
FrigaT
2026-01-05 00:29:19 +03:00
committed by FrigaT
parent 76a09d80d4
commit d0653c2098
105 changed files with 6729 additions and 0 deletions

View File

@@ -0,0 +1,359 @@
<Window
x:Class="SQLVision.UI.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:SQLVision.UI.Controls"
xmlns:charts="using:LiveChartsCore.SkiaSharpView.WinUI"
xmlns:muxc="using:Microsoft.UI.Xaml.Controls"
xmlns:converters="using:SQLVision.UI.Converters"
mc:Ignorable="d"
Title="SQLVision"
Width="1200"
Height="800"
MinWidth="800"
MinHeight="600">
<Window.Resources>
<converters:BooleanToVisibilityConverter x:Key="BoolToVisibility"/>
<converters:InverseBooleanConverter x:Key="InverseBool"/>
<Style TargetType="Button" x:Key="IconButtonStyle">
<Setter Property="Margin" Value="2"/>
<Setter Property="Padding" Value="8,4"/>
<Setter Property="MinWidth" Value="80"/>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Панель инструментов -->
<CommandBar Grid.Row="0" DefaultLabelPosition="Right">
<AppBarButton
Icon="Refresh"
Label="Обновить скрипты"
Command="{x:Bind ViewModel.LoadScriptsCommand}"/>
<AppBarSeparator/>
<AppBarButton
Icon="Play"
Label="Выполнить"
Command="{x:Bind ViewModel.ExecuteCommand}"
IsEnabled="{x:Bind ViewModel.ExecuteCommand.IsRunning, Converter={StaticResource InverseBool}, Mode=OneWay}"/>
<AppBarButton
Icon="Save"
Label="Экспорт"
Command="{x:Bind ViewModel.ExportCommand}"/>
<AppBarButton
Icon="Copy"
Label="Копировать SQL"
Command="{x:Bind ViewModel.CopySqlCommand}"/>
<AppBarSeparator/>
<AppBarButton
Icon="Clear"
Label="Очистить результаты"
Command="{x:Bind ViewModel.ClearResultsCommand}"/>
<AppBarSeparator/>
<AppBarButton
Icon="SaveLocal"
Label="Сохранить параметры"
Command="{x:Bind ViewModel.SaveParametersCommand}"/>
<AppBarButton
Icon="OpenLocal"
Label="Загрузить параметры"
Command="{x:Bind ViewModel.LoadParametersCommand}"/>
<CommandBar.Content>
<TextBlock
Text="SQLVision"
FontSize="18"
FontWeight="SemiBold"
Margin="12,0"/>
</CommandBar.Content>
</CommandBar>
<!-- Основное содержимое -->
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" MinWidth="250"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- Левая панель: скрипты и параметры -->
<Grid Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Поиск -->
<AutoSuggestBox
Grid.Row="0"
PlaceholderText="Поиск скриптов..."
Text="{x:Bind ViewModel.SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Margin="8">
<AutoSuggestBox.QueryIcon>
<SymbolIcon Symbol="Find"/>
</AutoSuggestBox.QueryIcon>
</AutoSuggestBox>
<!-- Дерево скриптов -->
<TreeView
Grid.Row="1"
ItemsSource="{x:Bind ViewModel.ScriptCategories, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedScript, Mode=TwoWay}"
Margin="8">
<TreeView.ItemTemplate>
<DataTemplate x:DataType="viewmodels:ScriptCategory">
<TreeViewItem
ItemsSource="{x:Bind Scripts}"
IsExpanded="{x:Bind IsExpanded, Mode=TwoWay}">
<TreeViewItem.Header>
<StackPanel Orientation="Horizontal" Spacing="8">
<TextBlock Text="{x:Bind Name}" FontWeight="SemiBold"/>
<TextBlock
Text="{x:Bind Scripts.Count}"
Foreground="Gray"
FontSize="12"/>
</StackPanel>
</TreeViewItem.Header>
<TreeViewItem.ItemTemplate>
<DataTemplate x:DataType="models:ScriptMetadata">
<TreeViewItem>
<StackPanel Orientation="Vertical" Spacing="2">
<TextBlock
Text="{x:Bind FileName}"
TextWrapping="Wrap"
FontWeight="Normal"/>
<TextBlock
Text="{x:Bind Description}"
FontSize="11"
Foreground="Gray"
TextWrapping="Wrap"
MaxLines="2"
TextTrimming="CharacterEllipsis"/>
</StackPanel>
</TreeViewItem>
</DataTemplate>
</TreeViewItem.ItemTemplate>
</TreeViewItem>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<!-- Параметры скрипта -->
<Border
Grid.Row="2"
BorderBrush="{ThemeResource SystemControlForegroundBaseLowBrush}"
BorderThickness="0,1,0,0"
Padding="8"
MaxHeight="400">
<ScrollViewer>
<StackPanel
x:Name="ParametersPanel"
Spacing="12"
Visibility="{x:Bind ViewModel.SelectedScript, Converter={StaticResource NullToVisibilityConverter}, Mode=OneWay}">
<TextBlock
Text="Параметры"
FontSize="16"
FontWeight="SemiBold"
Margin="0,0,0,8"/>
<ItemsControl ItemsSource="{x:Bind ViewModel.Parameters, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="viewmodels:ParameterViewModel">
<StackPanel Spacing="4" Margin="0,0,0,8">
<ContentPresenter Content="{x:Bind Control}"/>
<TextBlock
Text="{x:Bind ValidationError}"
Foreground="Red"
FontSize="11"
Visibility="{x:Bind HasError, Converter={StaticResource BoolToVisibility}}"/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</Border>
</Grid>
<!-- Splitter -->
<GridSplitter
Grid.Column="1"
Width="4"
HorizontalAlignment="Stretch"
Background="Transparent"/>
<!-- Правая панель: результаты -->
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- Вкладки с результатами -->
<muxc:TabView
Grid.Row="0"
ItemsSource="{x:Bind ViewModel.ResultTabs, Mode=OneWay}"
SelectedItem="{x:Bind ViewModel.SelectedResultTab, Mode=TwoWay}"
TabWidthMode="SizeToContent"
CanReorderTabs="True"
CanCloseTabs="True"
TabCloseRequested="OnTabCloseRequested">
<muxc:TabView.TabItemTemplate>
<DataTemplate x:DataType="viewmodels:ResultTabViewModel">
<muxc:TabViewItem
Header="{x:Bind Title}"
IconSource="{x:Bind IconSource}">
<ScrollViewer
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto">
<ContentPresenter Content="{x:Bind Content}"/>
</ScrollViewer>
<muxc:TabViewItem.ContextFlyout>
<MenuFlyout>
<MenuFlyoutItem
Text="Экспорт"
Command="{x:Bind ExportCommand}"
Icon="Save"/>
<MenuFlyoutItem
Text="Копировать данные"
Command="{x:Bind CopyDataCommand}"
Icon="Copy"/>
<MenuFlyoutSeparator/>
<MenuFlyoutItem
Text="Закрыть"
Click="OnCloseTabClick"/>
</MenuFlyout>
</muxc:TabViewItem.ContextFlyout>
</muxc:TabViewItem>
</DataTemplate>
</muxc:TabView.TabItemTemplate>
</muxc:TabView>
<!-- Панель истории -->
<Grid
Grid.Row="1"
Visibility="{x:Bind ViewModel.History.Count, Converter={StaticResource CountToVisibilityConverter}, Mode=OneWay}">
<Expander
Header="История выполненных запросов"
IsExpanded="False"
Margin="8">
<ListView
ItemsSource="{x:Bind ViewModel.History, Mode=OneWay}"
MaxHeight="200"
SelectionMode="None">
<ListView.ItemTemplate>
<DataTemplate x:DataType="models:ExecutionHistoryItem">
<Grid Padding="8" BorderBrush="LightGray" BorderThickness="0,0,0,1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0">
<TextBlock
Text="{x:Bind ScriptName}"
FontWeight="SemiBold"/>
<TextBlock
Text="{x:Bind ExecutionTime, StringFormat='{}{0:dd.MM.yyyy HH:mm:ss}'}"
FontSize="11"
Foreground="Gray"/>
</StackPanel>
<TextBlock
Grid.Column="1"
Text="{x:Bind Duration, StringFormat='{}{0:mm\\:ss}'}"
Margin="8,0"
VerticalAlignment="Center"/>
<TextBlock
Grid.Column="2"
Text="{x:Bind RowCount, StringFormat='{}Строк: {0}'}"
VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Expander>
</Grid>
</Grid>
</Grid>
<!-- Статус бар -->
<StatusBar Grid.Row="2">
<StatusBarItem>
<ProgressRing
Width="16"
Height="16"
IsActive="{x:Bind ViewModel.IsBusy, Mode=OneWay}"/>
</StatusBarItem>
<StatusBarItem>
<TextBlock Text="{x:Bind ViewModel.StatusMessage, Mode=OneWay}"/>
</StatusBarItem>
<StatusBarItem HorizontalAlignment="Right">
<TextBlock>
<Run Text="Скриптов:"/>
<Run Text="{x:Bind ViewModel.ScriptCategories.Sum(c => c.Scripts.Count), Mode=OneWay}"/>
<Run Text="|"/>
<Run Text="Вкладок:"/>
<Run Text="{x:Bind ViewModel.ResultTabs.Count, Mode=OneWay}"/>
</TextBlock>
</StatusBarItem>
</StatusBar>
<!-- Прогресс выполнения -->
<Grid
Grid.Row="0"
Grid.RowSpan="3"
Background="#CC000000"
Visibility="{x:Bind ViewModel.IsBusy, Converter={StaticResource BoolToVisibility}, Mode=OneWay}">
<Border
Background="{ThemeResource SystemControlBackgroundAltHighBrush}"
CornerRadius="8"
Padding="24"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<StackPanel Spacing="16" HorizontalAlignment="Center">
<ProgressRing Width="40" Height="40" IsActive="True"/>
<TextBlock
Text="Выполнение запроса..."
HorizontalAlignment="Center"/>
</StackPanel>
</Border>
</Grid>
</Grid>
</Window>

View File

@@ -0,0 +1,169 @@
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using SQLVision.Core.Models;
using SQLVision.Services;
using System;
using System.Collections.Generic;
using System.IO;
using Windows.Graphics;
namespace SQLVision.UI.Views
{
public sealed partial class MainWindow : Window
{
// Õðàíèì òåêóùèå ìåòàäàííûå âûáðàííîãî ñêðèïòà
private ScriptMetadata currentMetadata;
public MainWindow()
{
this.InitializeComponent();
SetWindowSize(1200, 800);
LoadScripts();
}
private void SetWindowSize(int width, int height)
{
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(this);
var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hwnd);
var appWindow = AppWindow.GetFromWindowId(windowId);
appWindow.Resize(new SizeInt32(width, height));
}
private void LoadScripts()
{
var folder = Path.Combine(AppContext.BaseDirectory, "Scripts");
if (!Directory.Exists(folder)) return;
foreach (var file in Directory.GetFiles(folder, "*.sql", SearchOption.AllDirectories))
{
var node = new TreeViewNode
{
Content = new ScriptTreeItem
{
DisplayName = Path.GetFileName(file),
FilePath = file
}
};
ScriptsTree.RootNodes.Add(node);
}
ScriptsTree.ItemInvoked += ScriptsTree_ItemInvoked;
}
private void ScriptsTree_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args)
{
if (args.InvokedItem is TreeViewNode node && node.Content is ScriptTreeItem item)
{
var file = item.FilePath;
var lines = File.ReadAllLines(file);
var parser = new SqlScriptParser();
currentMetadata = parser.Parse(lines); // ñîõðàíÿåì â ïîëå
RenderParameters(currentMetadata);
OutputTabs.TabItems.Clear();
foreach (var output in currentMetadata.Outputs)
{
var tab = new TabViewItem
{
Header = output.Description,
Content = new TextBlock { Text = $"Âûâîä: {output.Type}" }
};
OutputTabs.TabItems.Add(tab);
}
}
}
private void RenderParameters(ScriptMetadata metadata)
{
ParametersPanel.Items.Clear();
foreach (var param in metadata.Parameters)
{
FrameworkElement control = null;
switch (param.Type)
{
case ParameterType.Int:
control = new TextBox { Header = param.Description, Text = param.DefaultValue ?? string.Empty };
break;
case ParameterType.String:
control = new TextBox { Header = param.Description, Text = param.DefaultValue ?? string.Empty };
break;
case ParameterType.DateTime:
control = new DatePicker
{
Header = param.Description,
SelectedDate = DateTime.TryParse(param.DefaultValue, out var dt) ? dt : DateTime.Now
};
break;
case ParameterType.Bool:
control = new CheckBox
{
Content = param.Description,
IsChecked = param.DefaultValue?.ToLower() == "true"
};
break;
case ParameterType.Table:
var combo = new ComboBox { Header = param.Description };
combo.Items.Add("Ìàãàçèí 1");
combo.Items.Add("Ìàãàçèí 2");
combo.Items.Add("Ìàãàçèí 3");
control = combo;
break;
}
param.Control = control;
ParametersPanel.Items.Add(control);
}
}
private Dictionary<string, object> CollectParameterValues()
{
var values = new Dictionary<string, object>();
if (currentMetadata == null) return values;
foreach (var param in currentMetadata.Parameters)
{
switch (param.Type)
{
case ParameterType.Int:
if (param.Control is TextBox tbInt && int.TryParse(tbInt.Text, out var intVal))
values[param.Name] = intVal;
break;
case ParameterType.String:
if (param.Control is TextBox tbStr)
values[param.Name] = tbStr.Text;
break;
case ParameterType.DateTime:
if (param.Control is DatePicker dp && dp.SelectedDate.HasValue)
values[param.Name] = dp.SelectedDate.Value;
break;
case ParameterType.Bool:
if (param.Control is CheckBox cb)
values[param.Name] = cb.IsChecked ?? false;
break;
case ParameterType.Table:
if (param.Control is ComboBox combo && combo.SelectedItem != null)
values[param.Name] = combo.SelectedItem.ToString();
break;
}
}
return values;
}
private void ExecuteButton_Click(object sender, RoutedEventArgs e)
{
var values = CollectParameterValues();
// Çäåñü ìîæíî ñôîðìèðîâàòü SQL ñ ïîäñòàíîâêîé ïàðàìåòðîâ
foreach (var kv in values)
{
Console.WriteLine($"{kv.Key} = {kv.Value}");
}
}
}
}