更新 UI 和 ViewModel 以改进用户体验和功能

- 将 MainWindow.xaml 中的窗口标题从 "MainWindow" 更改为 "Excel助手"
- 调整了 MainWindow.xaml 中第一个 Grid 列的最小宽度,从 200 改为 100
- 删除了 MainWindow.xaml 中 SideMenu 的两个菜单项:“已导入的数据”和“查询”
- 更新了 MainWindow.xaml 中控件的 Padding、Background、BorderBrush 和 Width 属性
- 将 MainWindow.xaml 中的 TextBlock 替换为 hc:TextBox,并设置了 BorderBrush 和 BorderThickness 属性
- 更新了 ImportExcelPage.xaml 中 Grid 列的最小宽度和 DataGrid 的 RowHeight 属性
- 删除了 ImportExcelPage.xaml 中的“列信息”TabItem,并添加了新的“Columns2Prompt”TabItem
- 将 ImportExcelPage.xaml 中的 StackPanel 替换为 hc:Card,并调整了布局和样式
- 更新了 SqlQueryPage.xaml 中控件的 Visibility 属性和 ListBoxItem 的注释格式
- 调整了 SqlQueryPage.xaml 中 DataGrid 的 ColumnWidth 属性和 ScrollViewer.HorizontalScrollBarVisibility 属性的位置
- 在 ImportViewModel.cs 中添加了对 ExcelHelper.Utils 命名空间的引用
- 在 ImportViewModel.cs 中添加了 PromptString 属性,并在导入 Excel 数据时调用 Excel2Prompt.ConverterToPrompt 方法生成提示字符串
- 在 MainViewModel.cs 中删除了对 DataListPage 和 SqlQueryPage 的引用
- 添加了新的 Excel2Prompt 类,用于将 Excel 数据转换为 AI 可读的提示格式
This commit is contained in:
lihanbo 2025-02-13 16:49:24 +08:00
parent b1a10fea30
commit d9b0f3bf1e
7 changed files with 257 additions and 167 deletions

View File

@ -7,7 +7,7 @@
xmlns:local="clr-namespace:ExcelHelper"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewmodels="clr-namespace:ExcelHelper.Views.ViewModels"
Title="MainWindow"
Title="Excel助手"
Width="800"
Height="450"
d:DataContext="{d:DesignInstance Type=viewmodels:MainViewModel}"
@ -15,7 +15,7 @@
mc:Ignorable="d">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" MinWidth="200" />
<ColumnDefinition Width="Auto" MinWidth="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<hc:SideMenu Grid.Column="0" AutoSelect="True">
@ -24,14 +24,6 @@
CommandParameter="ImportExcelPage"
Header="导入"
IsSelected="True" />
<hc:SideMenuItem
Command="{Binding SideMenuSelectCommand}"
CommandParameter="DataListPage"
Header="已导入的数据" />
<hc:SideMenuItem
Command="{Binding SideMenuSelectCommand}"
CommandParameter="SqlQueryPage"
Header="查询" />
</hc:SideMenu>
<Frame
x:Name="MainFrame"
@ -45,13 +37,13 @@
FontSize="16"
Visibility="Collapsed">
<Border
Padding="0"
Padding="10"
Background="White"
BorderBrush="Gray"
BorderThickness="1"
CornerRadius="5">
<hc:SimpleStackPanel
Width="300"
Width="Auto"
ClipToBounds="True"
Orientation="Vertical">
<Grid>
@ -63,12 +55,12 @@
<Border
Grid.Row="0"
Margin="0,0,0,10"
Background="#d8d8d8"
Padding="2"
Background="#fff"
BorderBrush="#d8d8d8"
BorderThickness="2"
CornerRadius="5">
<DockPanel
Grid.Row="0"
Width="300"
HorizontalAlignment="Left">
<DockPanel Grid.Row="0" HorizontalAlignment="Stretch">
<TextBlock
Margin="5,0"
VerticalAlignment="Center"
@ -82,16 +74,21 @@
DockPanel.Dock="Right" />
</DockPanel>
</Border>
<TextBlock
<hc:TextBox
Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
BorderBrush="Transparent"
BorderThickness="0"
IsReadOnly="True"
Text="{Binding ErrorMessage}"
TextWrapping="Wrap" />
<Border
Grid.Row="2"
Margin="0,10,0,0"
Background="#b2b2b2"
Background="#fff"
BorderBrush="#b2b2b2"
BorderThickness="1"
CornerRadius="5">
<Grid>
<StackPanel

117
Utils/Excel2Prompt.cs Normal file
View File

@ -0,0 +1,117 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ExcelHelper.Utils;
public class Excel2Prompt
{
/// <summary>
/// 将excel数据转化为Ai可读的Prompt格式
/// </summary>
/// <param name="excelData"></param>
/// <param name="columns"></param>
/// <returns></returns>
public static string ConverterToPrompt(IEnumerable<dynamic> excelData, IEnumerable<string> columns)
{
/*
:
- Column '' (Excel Column A)
- Type: object
- Unique values: 30462
- Sample values: ['103526033584143963', '103526050570323185', '103525904231101189']
- Missing values: 8278
- Column '' (Excel Column B)
- Type: object
- Unique values: 11911
- Sample values: ['200012590592083', '200012844542760', '200012745140484']
- Missing values: 26899
- Column '' (Excel Column C)
- Type: object
- Unique values: 8
- Sample values: ['', '', '']
- Missing values: 7007
*/
var prompts = new List<string>();
var dataList = excelData.ToList();
foreach (var column in columns)
{
var columnData = dataList.Select(row =>
{
var rowDict = (IDictionary<string, object>)row;
return rowDict.ContainsKey(column) ? rowDict[column] : null;
}).ToList();
var nonNullData = columnData.Where(value => value != null).ToList();
var missingValues = columnData.Count - nonNullData.Count;
var uniqueValues = nonNullData.Distinct().Count();
var sampleValues = nonNullData.Take(3).Select(value => value.ToString().Replace("\n"," ")).ToArray();
// 自动识别数据类型
var dataType = GetColumnDataType(nonNullData);
var prompt = $"- Column '{column.Replace("\n", " ")}'\n" +
$" - Type: {dataType}\n" +
$" - Sample values: ['{string.Join("', '", sampleValues)}']";
//$" - Unique values: {uniqueValues}\n" +
//$" - Missing values: {missingValues}\n";
prompts.Add(prompt);
}
return string.Join(Environment.NewLine + Environment.NewLine, prompts);
}
private static string GetColumnDataType(List<object> data)
{
bool isInt = true;
bool isLong = true;
bool isDouble = true;
bool isDateTime = true;
bool isBool = true;
foreach (var item in data)
{
var str = item.ToString();
// 检查是否为 Int
if (!int.TryParse(str, out _))
isInt = false;
// 检查是否为 Long
if (!long.TryParse(str, out _))
isLong = false;
// 检查是否为 Double
if (!double.TryParse(str, out _))
isDouble = false;
// 检查是否为 DateTime
if (!DateTime.TryParse(str, out _))
isDateTime = false;
// 检查是否为 Bool
if (!bool.TryParse(str, out _))
isBool = false;
}
if (isInt)
return "int";
else if (isLong)
return "int64";
else if (isDouble)
return "double";
else if (isDateTime)
return "datetime";
else if (isBool)
return "bool";
else
return "string";
}
}

View File

@ -47,8 +47,11 @@
DragLeave="Grid_DragLeave"
Drop="Grid_Drop">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" MinWidth="800" />
<ColumnDefinition Width="370" MinWidth="370" />
<ColumnDefinition Width="*" MinWidth="200" />
<ColumnDefinition
Width="370"
MinWidth="370"
MaxWidth="600" />
</Grid.ColumnDefinitions>
<ContentPresenter
x:Name="DropFileMask"
@ -69,154 +72,123 @@
<hc:TabControl Grid.Column="0" Margin="5,5,7,5">
<hc:TabItem Header="预览">
<DataGrid
x:Name="ExcelDataPreviewGrid"
x:Name="ExcelDataPreviewGrid"
AutoGenerateColumns="False"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
IsReadOnly="True"
ItemsSource="{Binding ExcelData, IsAsync=True}"
RowHeight="NaN"
ScrollViewer.CanContentScroll="True"
VirtualizingPanel.IsContainerVirtualizable="True"
VirtualizingPanel.IsVirtualizing="True"
VirtualizingPanel.VirtualizationMode="Recycling" />
</hc:TabItem>
<hc:TabItem Header="列信息">
<DataGrid
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding TableColumns, IsAsync=True}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding ExcelColumnName}" Header="列名称" />
<DataGridTextColumn Binding="{Binding TableColumnName}" Header="生成表的列名称" />
</DataGrid.Columns>
</DataGrid>
<hc:TabItem Header="Columns2Prompt">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<hc:TextBox
Grid.Row="1"
Padding="5"
VerticalContentAlignment="Top"
AcceptsReturn="True"
Text="{Binding PromptString}"
TextWrapping="Wrap"
VerticalScrollBarVisibility="Auto" />
</Grid>
</hc:TabItem>
</hc:TabControl>
<GridSplitter
Grid.Column="0"
Width="2"
Margin="0,5" />
<StackPanel Grid.Column="1"
Margin="5">
<GroupBox Padding="5">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="5,0"
VerticalAlignment="Center"
Text="参数配置" />
<Button Style="{StaticResource ButtonPrimary}"
VerticalAlignment="Center"
Command="{Binding ReLoadExcelCommand}"
Content="重新读取Excel" />
</StackPanel>
</GroupBox.Header>
<hc:SimpleStackPanel SnapsToDevicePixels="True">
<hc:SimpleStackPanel.Resources>
<Style TargetType="{x:Type hc:SimpleStackPanel}">
<Setter Property="Margin" Value="5,0" />
</Style>
</hc:SimpleStackPanel.Resources>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label Content="指定的Sheet" />
<hc:ComboBox
MinWidth="60"
MaxWidth="180"
Margin="0"
ItemsSource="{Binding Sheets}"
SelectedValue="{Binding SelectedSheetName}" />
<Button
Margin="0"
VerticalAlignment="Center"
Command="{Binding ReadSheetsCommand}"
Content="刷新" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Height="Auto" Orientation="Horizontal">
<Label Content="起始位置:" />
<hc:TextBox MinWidth="40" Text="{Binding StartCell}" />
<CheckBox Content="包含列头" IsChecked="{Binding UseHeaderRow}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label Content="结束位置:" />
<hc:TextBox MinWidth="40" Text="{Binding EndCell}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label>
<CheckBox Content="合并的单元格进行填充" IsChecked="{Binding FillMergedCells}" />
</Label>
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label Content="最大读取行数:" />
<hc:NumericUpDown
MinWidth="40"
Minimum="0"
Maximum="300"
Value="{Binding MaxRow}" />
<TextBlock Margin="5,0" Text="预览时最大读取300行" VerticalAlignment="Center"/>
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Width="Auto" Orientation="Horizontal">
<Label Content="当前文件路径:" />
<!--<hc:TextBox MinWidth="200" />-->
<hc:ComboBox
MaxWidth="200"
ItemsSource="{Binding ExcelFiles}"
SelectedValue="{Binding CurrentFilePath}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel IsEnabled="False" Orientation="Horizontal">
<Label Content="Excel密码" />
<PasswordBox MinWidth="60" utils:PasswordBoxHelper.Password="{Binding ExcelPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="?" ToolTip="未加密留空多个Excel如果不同密码请分批次导入" />
</hc:SimpleStackPanel>
<hc:Card
Grid.Column="1"
Width="Auto"
Margin="5"
HorizontalAlignment="Stretch">
<hc:Card.Header>
<hc:SimpleStackPanel
Margin="5"
HorizontalAlignment="Center"
Orientation="Horizontal">
<TextBlock
Margin="5,0"
VerticalAlignment="Center"
Text="参数配置" />
<Button
VerticalAlignment="Center"
Command="{Binding ReLoadExcelCommand}"
Content="重新读取Excel"
Style="{StaticResource ButtonPrimary}" />
</hc:SimpleStackPanel>
</GroupBox>
<GroupBox Header="数据库配置" Padding="5">
<StackPanel Orientation="Vertical">
<StackPanel Orientation="Horizontal">
<Label Content="数据库类型"/>
<hc:ComboBox SelectedValuePath="Tag" SelectedValue="{Binding DbType}" MinWidth="100" >
<ComboBoxItem Tag="Sqlite" Content="Sqlite"/>
<ComboBoxItem Tag="SqlServer" Content="SqlServer"/>
<!--<ComboBoxItem Tag="MySql" Content="MySql/MariaDB"/>
<ComboBoxItem Tag="MongoDB" Content="MongoDB"/>
<ComboBoxItem Tag="PostgreSql" Content="PostgreSql"/>-->
</hc:ComboBox>
<CheckBox Content="加密连接" IsChecked="{Binding IsEncrypt}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<Label Content="地址:"/>
<hc:TextBox MinWidth="150" Text="{Binding DbServer}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="端口:"/>
<hc:TextBox MinWidth="50" Text="{Binding DbPort}" />
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal">
<Label Content="账号:"/>
<hc:TextBox MinWidth="100" Text="{Binding DbUser}" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="密码:"/>
<PasswordBox MinWidth="100" utils:PasswordBoxHelper.Password="{Binding DbPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="数据库:"/>
<hc:TextBox MinWidth="100" Text="{Binding DbName}" />
<Button Content="测试连接" Command="{Binding TestDbConnCommand}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Content="导入后的数据表名称:"/>
<hc:TextBox MinWidth="100" Text="" />
<Button Style="{StaticResource ButtonPrimary}" Content="导入数据"/>
</StackPanel>
</StackPanel>
</GroupBox>
</StackPanel>
</hc:Card.Header>
<hc:SimpleStackPanel SnapsToDevicePixels="True">
<hc:SimpleStackPanel.Resources>
<Style TargetType="{x:Type hc:SimpleStackPanel}">
<Setter Property="Margin" Value="5,5" />
</Style>
</hc:SimpleStackPanel.Resources>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label Content="指定的Sheet" />
<hc:ComboBox
MinWidth="60"
MaxWidth="180"
Margin="0"
ItemsSource="{Binding Sheets}"
SelectedValue="{Binding SelectedSheetName}" />
<Button
Margin="0"
VerticalAlignment="Center"
Command="{Binding ReadSheetsCommand}"
Content="刷新" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Height="Auto" Orientation="Horizontal">
<Label Content="起始位置:" />
<hc:TextBox MinWidth="40" Text="{Binding StartCell}" />
<CheckBox Content="包含列头" IsChecked="{Binding UseHeaderRow}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label Content="结束位置:" />
<hc:TextBox MinWidth="40" Text="{Binding EndCell}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label>
<CheckBox Content="合并的单元格进行填充" IsChecked="{Binding FillMergedCells}" />
</Label>
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<Label Content="最大读取行数:" />
<hc:NumericUpDown
MinWidth="40"
Maximum="300"
Minimum="0"
Value="{Binding MaxRow}" />
<TextBlock
Margin="5,0"
VerticalAlignment="Center"
Text="预览时最大读取300行" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Width="Auto" Orientation="Horizontal">
<Label Content="当前文件路径:" />
<!--<hc:TextBox MinWidth="200" />-->
<hc:ComboBox
MaxWidth="200"
ItemsSource="{Binding ExcelFiles}"
SelectedValue="{Binding CurrentFilePath}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel IsEnabled="False" Orientation="Horizontal">
<Label Content="Excel密码" />
<PasswordBox MinWidth="60" utils:PasswordBoxHelper.Password="{Binding ExcelPassword, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
<Button Content="?" ToolTip="未加密留空多个Excel如果不同密码请分批次导入" />
</hc:SimpleStackPanel>
</hc:SimpleStackPanel>
</hc:Card>
</Grid>
</Page>

View File

@ -29,13 +29,15 @@
<TextBlock
Margin="10"
VerticalAlignment="Center"
Text="储存类型:" />
Text="储存类型:"
Visibility="Collapsed" />
<hc:ComboBox
x:Name="SqlQueryComboBox"
Width="200"
Margin="10"
DisplayMemberPath="Name"
SelectedValuePath="Sql" />
SelectedValuePath="Sql"
Visibility="Collapsed" />
<Button
x:Name="ExecuteButton"
Margin="10"
@ -60,9 +62,9 @@
Grid.Column="1"
Margin="5"
SelectionChanged="FunctionListBox_SelectionChanged">
<ListBoxItem Content="INSTALL spatial; LOAD spatial;" />
<ListBoxItem Content="INSTALL spatial; LOAD spatial;" />
<ListBoxItem Content="st_read()" />
<!-- ...add more functions if needed... -->
<!-- ...add more functions if needed... -->
</ListBox>
<DataGrid
x:Name="ResultDataGrid"
@ -71,13 +73,11 @@
Grid.ColumnSpan="2"
Margin="5"
HorizontalAlignment="Stretch"
ColumnWidth="*"
hc:DataGridAttach.CanUnselectAllWithBlankArea="True"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
MinColumnWidth="100" />
<StackPanel
Grid.Row="3"
Orientation="Horizontal">
ColumnWidth="*"
MinColumnWidth="100"
ScrollViewer.HorizontalScrollBarVisibility="Auto" />
<StackPanel Grid.Row="3" Orientation="Horizontal">
<TextBlock
x:Name="StatusTextBlock"
Margin="5"

View File

@ -37,10 +37,12 @@ namespace ExcelHelper.Views.Pages
var xshd = HighlightingLoader.LoadXshd(reader);
var highlighting = HighlightingLoader.Load(xshd, HighlightingManager.Instance);
TextEditor.SyntaxHighlighting = highlighting;
}
}
// 替换 DuckDBDataAdapter 使用 DataTable.Load 方法
private void ExecuteButton_Click(object sender, RoutedEventArgs e)
{
@ -88,6 +90,6 @@ namespace ExcelHelper.Views.Pages
var caretOffset = TextEditor.CaretOffset;
TextEditor.Document.Insert(caretOffset, functionText);
}
}
}
}
}

View File

@ -9,6 +9,7 @@ using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using ExcelHelper.Message;
using ExcelHelper.Model;
using ExcelHelper.Utils;
using MiniExcelLibs;
using MiniExcelLibs.OpenXml;
using SqlSugar;
@ -75,6 +76,8 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
SelectedSheetName = Sheets.FirstOrDefault(it => it.Equals(currentSheetName));
}
MaxRow = ExcelData.Count();
PromptString = Excel2Prompt.ConverterToPrompt(ExcelData, columns);
}
catch (Exception ex)
{
@ -343,6 +346,7 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
[ObservableProperty]
private string _dbName;
[ObservableProperty]
private string _promptString;
#endregion
}

View File

@ -21,8 +21,6 @@ public partial class MainViewModel : ObservableObject, IViewModel
var targetPage = itemTag switch
{
"ImportExcelPage" => typeof(ImportExcelPage),
"DataListPage" => typeof(DataListPage),
"SqlQueryPage" => typeof(SqlQueryPage),
_ => null
};
if (targetPage != null)