This commit is contained in:
lihanbo 2024-10-29 16:57:50 +08:00
parent 05b41a5685
commit bad4552f16
14 changed files with 478 additions and 259 deletions

View File

@ -50,8 +50,11 @@
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="SQLite" Version="3.13.0" />
<PackageReference Include="SqlSugarCore" Version="5.1.4.171-preview01" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
<PackageReference Include="System.Data.DataSetExtensions" Version="4.5.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.0-rc.2.24473.5" />
<PackageReference Include="TinyPinyin" Version="1.1.0" />
</ItemGroup>
</Project>

View File

@ -33,24 +33,30 @@
x:Name="MainFrame"
Grid.Column="1"
NavigationUIVisibility="Hidden" />
<hc:Dialog Background="Transparent" x:Name="MessageDialog" Visibility="Collapsed" Grid.Column="0" Grid.ColumnSpan="2">
<hc:Dialog FontSize="16" Background="Transparent" x:Name="MessageDialog" Visibility="Collapsed" Grid.Column="0" Grid.ColumnSpan="2">
<Border BorderBrush="Gray" Background="White" BorderThickness="1" CornerRadius="5" Padding="0">
<hc:SimpleStackPanel ClipToBounds="True" Orientation="Vertical" Height="300" Width="300" >
<Grid Margin="5" >
<hc:SimpleStackPanel ClipToBounds="True" Orientation="Vertical" Width="300" >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="*" MinHeight="200"/>
<RowDefinition Height="*" MinHeight="100"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Border CornerRadius="5" Grid.Row="0" Margin="0,0,0,10" Background="#d8d8d8">
<DockPanel Width="300" Grid.Row="0" HorizontalAlignment="Left" >
<TextBlock Margin="5,0" DockPanel.Dock="Left" Text="12312312" VerticalAlignment="Center"/>
<Button Click="DialogCloseBtn_Click" Margin="15,0" HorizontalAlignment="Right" DockPanel.Dock="Right" Content="X"/>
<TextBlock Margin="5,0" DockPanel.Dock="Left" Text="提示" VerticalAlignment="Center"/>
<Button Click="DialogCloseBtn_Click" Margin="5,0" HorizontalAlignment="Right" DockPanel.Dock="Right" Content="X"/>
</DockPanel>
</Border>
<TextBlock Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{Binding ErrorMessage}"/>
<StackPanel Grid.Row="2" VerticalAlignment="Center" HorizontalAlignment="Right">
<Border CornerRadius="5" Grid.Row="2" Margin="0,10,0,0" Background="#b2b2b2">
<Grid>
<StackPanel Margin="5" VerticalAlignment="Center" HorizontalAlignment="Right">
<Button x:Name="DialogCloseBtn" Content="确定" Click="DialogCloseBtn_Click"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</hc:SimpleStackPanel>
</Border>
</hc:Dialog>

View File

@ -5,14 +5,14 @@ using ExcelHelper.Services;
using ExcelHelper.Views;
using ExcelHelper.Views.ViewModels;
namespace ExcelHelper
namespace ExcelHelper;
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window, IView
{
/// <summary>
/// MainWindow.xaml 的交互逻辑
/// </summary>
public partial class MainWindow : Window, IView
{
private MainViewModel ViewModel;
private readonly MainViewModel ViewModel;
public MainWindow(MainViewModel viewModel, NavigationService navigationService)
{
InitializeComponent();
@ -34,5 +34,4 @@ namespace ExcelHelper
MessageDialog.Visibility = System.Windows.Visibility.Collapsed;
}
}
}

18
Model/TableColumnModel.cs Normal file
View File

@ -0,0 +1,18 @@
namespace ExcelHelper.Model
{
public class TableColumnModel
{
public string ExcelColumnName
{
get; set;
}
public string TableColumnName
{
get; set;
}
public string DataType
{
get; set;
}
}
}

View File

@ -3,10 +3,10 @@ using System.Threading.Tasks;
using ExcelHelper.Views.Pages;
using Microsoft.Extensions.Hosting;
namespace ExcelHelper.Services
namespace ExcelHelper.Services;
public class ExcelHelperHostedService : IHostedService
{
public class ExcelHelperHostedService : IHostedService
{
public Task StartAsync(CancellationToken cancellationToken)
{
var mainWindow = App.AppHost.Get<MainWindow>();
@ -23,5 +23,4 @@ namespace ExcelHelper.Services
return Task.CompletedTask;
}
}
}

View File

@ -2,10 +2,10 @@
using System.Windows.Controls;
using ExcelHelper.Views;
namespace ExcelHelper.Services
namespace ExcelHelper.Services;
public class NavigationService
{
public class NavigationService
{
private Frame _frame;
public static string currentView;
@ -31,5 +31,4 @@ namespace ExcelHelper.Services
_frame.NavigationService.Navigate(page);
}
}
}
}

View File

@ -57,8 +57,7 @@ public static class PasswordBoxHelper
private static void OnAttachPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
if (passwordBox == null)
if (sender is not PasswordBox passwordBox)
{
return;
}

View File

@ -1,6 +1,5 @@
namespace ExcelHelper.Views
namespace ExcelHelper.Views;
internal interface IView
{
internal interface IView
{
}
}

View File

@ -1,6 +1,5 @@
namespace ExcelHelper.Views
namespace ExcelHelper.Views;
internal interface IViewModel
{
internal interface IViewModel
{
}
}

View File

@ -1,15 +1,14 @@
using System.Windows.Controls;
namespace ExcelHelper.Views.Pages
namespace ExcelHelper.Views.Pages;
/// <summary>
/// DataListPage.xaml 的交互逻辑
/// </summary>
public partial class DataListPage : Page, IView
{
/// <summary>
/// DataListPage.xaml 的交互逻辑
/// </summary>
public partial class DataListPage : Page, IView
{
public DataListPage()
{
InitializeComponent();
}
}
}

View File

@ -70,7 +70,6 @@
<hc:TabItem Header="预览">
<DataGrid
x:Name="ExcelDataPreviewGrid"
hc:DataGridAttach.ShowRowNumber="True"
AutoGenerateColumns="False"
EnableColumnVirtualization="True"
EnableRowVirtualization="True"
@ -85,9 +84,10 @@
<DataGrid
AutoGenerateColumns="False"
IsReadOnly="True"
ItemsSource="{Binding ExcelColumns, IsAsync=True}">
ItemsSource="{Binding TableColumns, IsAsync=True}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Header}" Header="列" />
<DataGridTextColumn Binding="{Binding ExcelColumnName}" Header="列名称" />
<DataGridTextColumn Binding="{Binding TableColumnName}" Header="生成表的列名称" />
</DataGrid.Columns>
</DataGrid>
</hc:TabItem>
@ -96,17 +96,17 @@
Grid.Column="0"
Width="2"
Margin="0,5" />
<GroupBox
Grid.Column="1"
Margin="5"
Padding="5">
<StackPanel Grid.Column="1"
Margin="5">
<GroupBox Padding="5">
<GroupBox.Header>
<StackPanel Orientation="Horizontal">
<TextBlock
Margin="5,0"
VerticalAlignment="Center"
Text="参数配置" />
<Button
<Button Style="{StaticResource ButtonPrimary}"
VerticalAlignment="Center"
Command="{Binding ReLoadExcelCommand}"
Content="重新读取Excel" />
@ -123,30 +123,38 @@
<Label Content="指定的Sheet" />
<hc:ComboBox
MinWidth="60"
MaxWidth="180"
Margin="0"
ItemsSource="{Binding Sheets}"
SelectedValue="{Binding SelectedSheetName}" />
<Button
Margin="0"
VerticalAlignment="Center"
Command=""
Content="读取" />
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 IsEnabled="False" Orientation="Horizontal">
<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="当前文件路径:" />
@ -156,14 +164,64 @@
ItemsSource="{Binding ExcelFiles}"
SelectedValue="{Binding CurrentFilePath}" />
</hc:SimpleStackPanel>
<hc:SimpleStackPanel Orientation="Horizontal">
<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>
</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>
</Grid>
</Page>

View File

@ -3,14 +3,14 @@ using CommunityToolkit.Mvvm.Messaging;
using ExcelHelper.Message;
using ExcelHelper.Views.ViewModels;
namespace ExcelHelper.Views.Pages
namespace ExcelHelper.Views.Pages;
/// <summary>
/// ImportExcelPage.xaml 的交互逻辑
/// </summary>
public partial class ImportExcelPage : Page, IView, IRecipient<UpdateDataGridColumnsMessage>
{
/// <summary>
/// ImportExcelPage.xaml 的交互逻辑
/// </summary>
public partial class ImportExcelPage : Page, IView, IRecipient<UpdateDataGridColumnsMessage>
{
private ImportViewModel ViewModel;
private readonly ImportViewModel ViewModel;
public ImportExcelPage(ImportViewModel viewModel)
{
InitializeComponent();
@ -71,5 +71,4 @@ namespace ExcelHelper.Views.Pages
}
}
}
}
}

View File

@ -2,12 +2,16 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Controls;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.Messaging;
using ExcelHelper.Message;
using ExcelHelper.Model;
using MiniExcelLibs;
using MiniExcelLibs.OpenXml;
using SqlSugar;
namespace ExcelHelper.Views.ViewModels;
@ -31,15 +35,23 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
IsLoading = false;
}
public async void ReadForExcel(string path)
public async void ReadForExcel(string path, bool isReload = false)
{
IsLoading = true;
//await Task.Factory.StartNew(async () =>
//{
try
{
if (string.IsNullOrEmpty(path)) return;
var excelData = await MiniExcel.QueryAsync(path, sheetName: SelectedSheetName, useHeaderRow: UseHeaderRow, startCell: StartCell);
var config = new OpenXmlConfiguration()
{
FillMergedCells = FillMergedCells
};
if (string.IsNullOrEmpty(path))
{
IsLoading = false;
return;
}
var excelData = await GetExcelData(path, config);
if (MaxRow != 0)
{
ExcelData = excelData.Take(Math.Min(MaxRow, 300));
@ -48,9 +60,20 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
{
ExcelData = excelData.Take(300);
}
var columns = MiniExcel.GetColumns(path, useHeaderRow: UseHeaderRow, startCell: StartCell, sheetName: SelectedSheetName);
var currentSheetName = SelectedSheetName;
var columns = MiniExcel.GetColumns(path, useHeaderRow: UseHeaderRow, startCell: StartCell, sheetName: currentSheetName, configuration: config);
GenColumns(columns);
GetSheets(path);
if (string.IsNullOrEmpty(currentSheetName))
{
SelectedSheetName = Sheets.FirstOrDefault();
}
else
{
SelectedSheetName = Sheets.FirstOrDefault(it => it.Equals(currentSheetName));
}
MaxRow = ExcelData.Count();
}
catch (Exception ex)
@ -58,18 +81,22 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
WeakReferenceMessenger.Default.Send(new ErrorDialogMessage($"出现了异常!{ex}"));
}
IsLoading = false;
//}).ContinueWith(x =>
// {
// });
}
public async Task<IEnumerable<dynamic>> GetExcelData(string path, Configuration config)
{
return string.IsNullOrEmpty(EndCell)
? await MiniExcel.QueryAsync(path, sheetName: SelectedSheetName, useHeaderRow: UseHeaderRow, startCell: StartCell, configuration: config)
: MiniExcel.QueryRange(path, sheetName: SelectedSheetName, useHeaderRow: UseHeaderRow, startCell: StartCell, endCell: EndCell, configuration: config);
}
private void GenColumns(IEnumerable<string> columns)
{
ExcelColumns.Clear();
TableColumns.Clear();
if (UseHeaderRow)
{
foreach (var columnName in columns)
{
var column = new DataGridTextColumn
@ -94,8 +121,20 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
ExcelColumns.Add(column);
}
}
int columnsSeq = 0;
foreach (var item in ExcelColumns)
{
TableColumns.Add(new TableColumnModel
{
ExcelColumnName = item.Header.ToString(),
TableColumnName = $"Column{columnsSeq}",
});
columnsSeq++;
}
if (ExcelColumns.Count > 0)
{
WeakReferenceMessenger.Default.Send(new UpdateDataGridColumnsMessage([.. ExcelColumns]));
}
else
{
WeakReferenceMessenger.Default.Send(new ErrorDialogMessage("未读取到列信息!"));
@ -105,31 +144,100 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
[RelayCommand]
private void OnReLoadExcel()
{
ReadForExcel(CurrentFilePath);
ReadForExcel(CurrentFilePath, isReload: true);
}
[RelayCommand]
private void OnReadSheets()
{
if (string.IsNullOrWhiteSpace(CurrentFilePath))
if (!string.IsNullOrWhiteSpace(CurrentFilePath))
{
GetSheets(CurrentFilePath);
SelectedSheetName = Sheets.FirstOrDefault();
}
}
private void GetSheets(string excelPath)
{
var sheets = MiniExcel.GetSheetNames(excelPath);
if (Sheets == null)
{
Sheets = new ObservableCollection<string>();
Sheets = [];
}
else
{
Sheets.Clear();
}
MiniExcel.GetSheetNames(excelPath)?.ForEach(sheetName =>
sheets.ForEach(sheetName =>
{
Sheets.Add(sheetName);
});
SelectedSheetName = Sheets.FirstOrDefault();
}
[RelayCommand]
private void OnTestDbConn()
{
try
{
var client = GetClient();
var tables = client.Ado.ExecuteCommand("select 1");
WeakReferenceMessenger.Default.Send(new ErrorDialogMessage("连接成功!"));
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(new ErrorDialogMessage($"连接失败!{ex}"));
}
}
private SqlSugarClient GetClient()
{
var connStr = GetConnStr(DbType);
if (string.IsNullOrEmpty(connStr))
{
WeakReferenceMessenger.Default.Send(new ErrorDialogMessage("未知的数据库类型!"));
return null;
}
try
{
var client = new SqlSugarClient(new ConnectionConfig
{
ConnectionString = connStr,
DbType = DbType switch
{
//"MySql" => SqlSugar.DbType.MySql,
"Sqlite" => SqlSugar.DbType.Sqlite,
"SqlServer" => SqlSugar.DbType.SqlServer,
_ => throw new Exception("未知的数据库类型")
},
IsAutoCloseConnection = true
});
return client;
//WeakReferenceMessenger.Default.Send(new ErrorDialogMessage("连接成功!"));
}
catch (Exception ex)
{
WeakReferenceMessenger.Default.Send(new ErrorDialogMessage($"连接失败!{ex}"));
return null;
}
}
private string GetConnStr(string dbType)
{
var encryptStr = "";
if (IsEncrypt)
{
encryptStr = "Encrypt=True;TrustServerCertificate=True;";
}
return dbType switch
{
//"MySql" => $"Server={DbServer};Port={DbPort};Database={DbName};User Id={DbUser};Password={DbPassword};{encryptStr}",
"SqlServer" => $"Server={DbServer},{DbPort};Database={DbName};User Id={DbUser};Password={DbPassword};{encryptStr}",
//"PostgreSql" => $"Server={DbServer};Port={DbPort};Database={DbName};User Id={DbUser};Password={DbPassword};{encryptStr}",
"Sqlite" => $"DataSource={DbServer};",
_ => ""
};
}
#region Props
@ -142,6 +250,9 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
[ObservableProperty]
private ObservableCollection<DataGridColumn> _excelColumns = [];
[ObservableProperty]
private ObservableCollection<TableColumnModel> _tableColumns = [];
/// <summary>
/// 是否正在加载
/// </summary>
@ -175,6 +286,13 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
/// </summary>
[ObservableProperty]
private string _endCell;
/// <summary>
/// 使用首行作为表头
/// </summary>
[ObservableProperty]
private bool _fillMergedCells = false;
/// <summary>
/// 最大行数
/// </summary>
@ -201,5 +319,30 @@ public partial class ImportViewModel : ObservableRecipient, IViewModel
[ObservableProperty]
private string _excelPassword;
[ObservableProperty]
private string _dbType;
/// <summary>
/// 加密连接
/// </summary>
[ObservableProperty]
private bool _isEncrypt;
[ObservableProperty]
private string _dbUser;
[ObservableProperty]
private string _dbPassword;
[ObservableProperty]
private string _dbServer;
[ObservableProperty]
private string _dbPort;
[ObservableProperty]
private string _dbName;
#endregion
}

View File

@ -3,10 +3,10 @@ using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using ExcelHelper.Views.Pages;
namespace ExcelHelper.Views.ViewModels
namespace ExcelHelper.Views.ViewModels;
public partial class MainViewModel : ObservableObject, IViewModel
{
public partial class MainViewModel : ObservableObject, IViewModel
{
/// <summary>
/// 异常信息
@ -30,5 +30,4 @@ namespace ExcelHelper.Views.ViewModels
}
}
}
}