Initial import of WPF migration tool

This commit is contained in:
chen qiang
2026-08-25 16:23:14 +08:00
commit 227f92c45b
47 changed files with 5204 additions and 0 deletions
@@ -0,0 +1,508 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
using henlo_migration.Common;
using henlo_migration.Enums;
using henlo_migration.Models;
using henlo_migration.Services;
namespace henlo_migration.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
// 四个 Tab 的数据
public TabServices TableTab { get; }
public TabServices BosTab { get; }
public TabServices PrcTab { get; }
public TabServices DataTab { get; }
// 当前激活的 TabServices
private TabServices? _currentTab;
private TabServices? CurrentTab
{
get => _currentTab;
set
{
_currentTab = value;
OnPropertyChanged();
OnPropertyChanged(nameof(TablesData));
OnPropertyChanged(nameof(BosTablesData));
OnPropertyChanged(nameof(StoredProcsData));
OnPropertyChanged(nameof(CurrentDataData));
}
}
// Tab 切换
private int _selectedTabIndex;
public int SelectedTabIndex
{
get => _selectedTabIndex;
set
{
_selectedTabIndex = value;
CurrentTab = value switch
{
0 => TableTab,
1 => BosTab,
2 => PrcTab,
3 => DataTab,
_ => TableTab
};
OnPropertyChanged();
OnPropertyChanged(nameof(TablesData));
OnPropertyChanged(nameof(BosTablesData));
OnPropertyChanged(nameof(StoredProcsData));
OnPropertyChanged(nameof(CurrentDataData));
OnPropertyChanged(nameof(CurrentTabName));
OnPropertyChanged(nameof(CurrentTabCount));
}
}
// 四个Tab数据源别名(供DataGrid绑定)
public ObservableCollection<TableRowModel> TablesData => TableTab.List;
public ObservableCollection<TableRowModel> BosTablesData => BosTab.List;
public ObservableCollection<TableRowModel> StoredProcsData => PrcTab.List;
public ObservableCollection<TableRowModel> CurrentDataData => DataTab.List;
public string CurrentTabName => SelectedTabIndex switch
{
0 => "表配置",
1 => "BOS配置",
2 => "存储过程",
3 => "表数据",
_ => ""
};
public string CurrentTabCount => CurrentTab?.List.Count.ToString() ?? "0";
// 选中的行
private TableRowModel? _selectedRow;
public TableRowModel? SelectedRow
{
get => _selectedRow;
set
{
_selectedRow = value;
OnPropertyChanged();
// 选中行时自动加载已有文件内容
LoadSelectedRowContent();
}
}
private void LoadSelectedRowContent()
{
if (_selectedRow == null || CurrentTab == null) return;
// 构建文件路径
string fileName = $"{_selectedRow.Name}_{CurrentTab.GetCodeExt()}.txt";
string filePath = Path.Combine(CurrentTab.GetContentFolderPath(), fileName);
// 始终设置 CurrentFileName,这样保存按钮可用
CurrentFileName = filePath;
if (File.Exists(filePath))
{
try
{
string content = File.ReadAllText(filePath);
CurrentFileContent = content;
SqlCode = content;
}
catch { CurrentFileContent = ""; SqlCode = ""; }
}
else
{
// 文件不存在,清空内容但保留文件路径
CurrentFileContent = "";
SqlCode = "";
}
}
// SQL 编辑区
private string _sqlCode = "";
public string SqlCode
{
get => _sqlCode;
set { _sqlCode = value; OnPropertyChanged(); }
}
// 选中行详情面板
private string _currentFileName = "";
public string CurrentFileName
{
get => _currentFileName;
set { _currentFileName = value; OnPropertyChanged(); OnPropertyChanged(nameof(HasCurrentFile)); (SaveCurrentContentCommand as RelayCommand)?.RaiseCanExecuteChanged(); }
}
private string _currentFileContent = "";
public string CurrentFileContent
{
get => _currentFileContent;
set { _currentFileContent = value; OnPropertyChanged(); OnPropertyChanged(nameof(HasCurrentFile)); }
}
public bool HasCurrentFile => !string.IsNullOrEmpty(CurrentFileName);
// 保存当前文件内容命令
public ICommand SaveCurrentContentCommand { get; }
// 输入框
private string _inputText = "";
public string InputText
{
get => _inputText;
set { _inputText = value; OnPropertyChanged(); }
}
// 数据库连接状态
private bool _origConnected;
public bool OrigConnected
{
get => _origConnected;
set
{
_origConnected = value;
OnPropertyChanged();
// 触发命令可用性更新
(ConnectOrigCommand as RelayCommand)?.RaiseCanExecuteChanged();
(DisconnectOrigCommand as RelayCommand)?.RaiseCanExecuteChanged();
}
}
private bool _destConnected;
public bool DestConnected
{
get => _destConnected;
set
{
_destConnected = value;
OnPropertyChanged();
(ConnectDestCommand as RelayCommand)?.RaiseCanExecuteChanged();
(DisconnectDestCommand as RelayCommand)?.RaiseCanExecuteChanged();
}
}
// 连接中状态
private bool _isConnecting;
public bool IsConnecting
{
get => _isConnecting;
set
{
_isConnecting = value;
OnPropertyChanged();
OnPropertyChanged(nameof(CanConnect));
(ConnectOrigCommand as RelayCommand)?.RaiseCanExecuteChanged();
(ConnectDestCommand as RelayCommand)?.RaiseCanExecuteChanged();
}
}
public bool CanConnect => !IsConnecting;
// 连接配置属性(密码改为明文显示,方便用户确认,保存时才加密写入)
private string _origIp = "";
public string OrigIp { get => _origIp; set { _origIp = value; OnPropertyChanged(); OnPropertyChanged(nameof(OrigConnDisplay)); } }
private string _origExam = "";
public string OrigExam { get => _origExam; set { _origExam = value; OnPropertyChanged(); OnPropertyChanged(nameof(OrigConnDisplay)); } }
private string _origUser = "";
public string OrigUser { get => _origUser; set { _origUser = value; OnPropertyChanged(); OnPropertyChanged(nameof(OrigConnDisplay)); } }
private string _origPwd = "";
public string OrigPwd { get => _origPwd; set { _origPwd = value; OnPropertyChanged(); } }
private string _destIp = "";
public string DestIp { get => _destIp; set { _destIp = value; OnPropertyChanged(); OnPropertyChanged(nameof(DestConnDisplay)); } }
private string _destExam = "";
public string DestExam { get => _destExam; set { _destExam = value; OnPropertyChanged(); OnPropertyChanged(nameof(DestConnDisplay)); } }
private string _destUser = "";
public string DestUser { get => _destUser; set { _destUser = value; OnPropertyChanged(); OnPropertyChanged(nameof(DestConnDisplay)); } }
private string _destPwd = "";
public string DestPwd { get => _destPwd; set { _destPwd = value; OnPropertyChanged(); } }
public string OrigConnDisplay => string.IsNullOrEmpty(OrigIp) ? "未配置" : $"{OrigIp}/{OrigExam}/{OrigUser}";
public string DestConnDisplay => string.IsNullOrEmpty(DestIp) ? "未配置" : $"{DestIp}/{DestExam}/{DestUser}";
// 密码遮罩显示(用于 TextBox 绑定)
public string OrigPwdDisplay => new string('●', OrigPwd?.Length ?? 0);
public string DestPwdDisplay => new string('●', DestPwd?.Length ?? 0);
// 命令
public ICommand ConnectOrigCommand { get; }
public ICommand ConnectDestCommand { get; }
public ICommand SaveOrigCommand { get; }
public ICommand SaveDestCommand { get; }
public ICommand DisconnectOrigCommand { get; }
public ICommand DisconnectDestCommand { get; }
public ICommand AddRowCommand { get; }
public ICommand OpenDataFolderCommand { get; }
public ICommand PreProcessCommand { get; }
public ICommand BatchReadCommand { get; }
public ICommand BatchWriteCommand { get; }
// 行操作命令
public ICommand ReadCommand { get; }
public ICommand WriteCommand { get; }
public ICommand DeleteCommand { get; }
public ICommand MoveUpCommand { get; }
public ICommand MoveDownCommand { get; }
public ICommand MoveTopCommand { get; }
public MainViewModel()
{
// 从配置加载连接信息
OrigIp = SysConfig.GetOrigIp();
OrigExam = SysConfig.GetOrigExam();
OrigUser = SysConfig.GetOrigUser();
OrigPwd = SysConfig.GetOrigPwd();
DestIp = SysConfig.GetDestIp();
DestExam = SysConfig.GetDestExam();
DestUser = SysConfig.GetDestUser();
DestPwd = SysConfig.GetDestPwd();
// 初始化四个 TabServices(索引文件在 from/,内容文件在 data/)
TableTab = new TabServices(new TabConfig(TabsEnum.表, CommonHelper.DataFromPath, CommonHelper.DataPath, "table.txt", CodeExt.creat,
DbServices.GetTableSql, (p, c) => SqlCode = c));
BosTab = new TabServices(new TabConfig(TabsEnum.BOS表配置, CommonHelper.DataFromPath, CommonHelper.DataPath, "bos.txt", CodeExt.bos,
DbServices.GetTableBos, (p, c) => SqlCode = c));
PrcTab = new TabServices(new TabConfig(TabsEnum.存储过程, CommonHelper.DataFromPath, CommonHelper.DataPath, "prc.txt", CodeExt.prc,
name => DbServices.GetPrcSql(name), (p, c) => SqlCode = c));
DataTab = new TabServices(new TabConfig(TabsEnum.表数据, CommonHelper.DataFromPath, CommonHelper.DataPath, "data.txt", CodeExt.data,
DbServices.GetTableData, (p, c) => SqlCode = c));
// Tab行点击 -> 显示SQL
foreach (var tab in new[] { TableTab, BosTab, PrcTab, DataTab })
{
tab.RowClickEvent += (path, code) =>
{
SqlCode = code;
CurrentFileName = path;
CurrentFileContent = code;
};
}
// 命令初始化
ConnectOrigCommand = new RelayCommand(async () => await ConnectOrigAsync(), () => !IsConnecting && !OrigConnected);
ConnectDestCommand = new RelayCommand(async () => await ConnectDestAsync(), () => !IsConnecting && !DestConnected);
SaveOrigCommand = new RelayCommand(SaveOrig);
SaveDestCommand = new RelayCommand(SaveDest);
DisconnectOrigCommand = new RelayCommand(DisconnectOrig, () => OrigConnected);
DisconnectDestCommand = new RelayCommand(DisconnectDest, () => DestConnected);
AddRowCommand = new RelayCommand(AddRow, () => CurrentTab != null && !string.IsNullOrWhiteSpace(InputText));
OpenDataFolderCommand = new RelayCommand(OpenDataFolder);
PreProcessCommand = new RelayCommand(PreProcess);
BatchReadCommand = new RelayCommand(async () => await BatchReadAsync());
BatchWriteCommand = new RelayCommand(async () => await BatchWriteAsync());
ReadCommand = new RelayCommand<TableRowModel>(async row => await ReadRowAsync(row));
WriteCommand = new RelayCommand<TableRowModel>(async row => await WriteRowAsync(row));
DeleteCommand = new RelayCommand<TableRowModel>(DeleteRow);
MoveUpCommand = new RelayCommand<TableRowModel>(MoveUp);
MoveDownCommand = new RelayCommand<TableRowModel>(MoveDown);
MoveTopCommand = new RelayCommand<TableRowModel>(MoveTop);
SaveCurrentContentCommand = new RelayCommand(SaveCurrentContent, () => HasCurrentFile);
// 默认选中第一个Tab
CurrentTab = TableTab;
}
private async Task ConnectOrigAsync()
{
IsConnecting = true;
try
{
SysConfig.SetOrig(OrigIp, OrigExam, OrigUser, OrigPwd);
SqlSugarHelper.Connect();
OrigConnected = SqlSugarHelper.CheckOrigConnect();
if (!OrigConnected) HandyControl.Controls.Growl.Warning("源数据库连接失败,请检查配置");
else HandyControl.Controls.Growl.Success("源数据库连接成功");
}
catch
{
OrigConnected = false;
HandyControl.Controls.Growl.Warning("源数据库连接失败");
}
finally { IsConnecting = false; }
}
private async Task ConnectDestAsync()
{
IsConnecting = true;
try
{
SysConfig.SetDest(DestIp, DestExam, DestUser, DestPwd);
SqlSugarHelper.Connect();
DestConnected = SqlSugarHelper.CheckDescConnect();
if (!DestConnected) HandyControl.Controls.Growl.Warning("目标数据库连接失败,请检查配置");
else HandyControl.Controls.Growl.Success("目标数据库连接成功");
}
catch
{
DestConnected = false;
HandyControl.Controls.Growl.Warning("目标数据库连接失败");
}
finally { IsConnecting = false; }
}
// 保存配置(只写 INI,不断开/重连)
private void SaveOrig() { SysConfig.SetOrig(OrigIp, OrigExam, OrigUser, OrigPwd); HandyControl.Controls.Growl.Success("源库配置已保存"); }
private void SaveDest() { SysConfig.SetDest(DestIp, DestExam, DestUser, DestPwd); HandyControl.Controls.Growl.Success("目标库配置已保存"); }
// 断开连接(只改状态,不真正关闭 SqlSugarScope,保持实例可用)
private void DisconnectOrig()
{
OrigConnected = false;
HandyControl.Controls.Growl.Info("已断开源数据库");
}
private void DisconnectDest()
{
DestConnected = false;
HandyControl.Controls.Growl.Info("已断开目标数据库");
}
private void AddRow()
{
if (!string.IsNullOrWhiteSpace(InputText))
{
CurrentTab?.AddRow(InputText.Trim().ToLower());
InputText = "";
}
}
private void SaveCurrentContent()
{
if (string.IsNullOrEmpty(CurrentFileName)) return;
try
{
File.WriteAllText(CurrentFileName, CurrentFileContent);
HandyControl.Controls.Growl.Success("保存成功");
}
catch (Exception ex)
{
HandyControl.Controls.Growl.Error($"保存失败:{ex.Message}");
}
}
private void OpenDataFolder()
{
Process.Start("explorer.exe", CommonHelper.DataPath);
}
private void PreProcess()
{
try { DbServices.PreProcess(); }
catch { }
}
private async Task BatchReadAsync()
{
if (CurrentTab == null) return;
foreach (var row in CurrentTab.List)
await CurrentTab.ReadRow(row);
}
/// <summary>批量写入断点:记录上次失败行的索引,下次从此处继续</summary>
private int _breakpointIndex = -1;
public int BreakpointIndex
{
get => _breakpointIndex;
set { _breakpointIndex = value; OnPropertyChanged(); OnPropertyChanged(nameof(HasBreakpoint)); }
}
public bool HasBreakpoint => _breakpointIndex >= 0;
private async Task BatchWriteAsync()
{
if (CurrentTab == null) return;
int startIdx = _breakpointIndex >= 0 ? _breakpointIndex : 0;
bool hasNewFailure = false;
for (int i = startIdx; i < CurrentTab.List.Count; i++)
{
var row = CurrentTab.List[i];
await CurrentTab.WriteRow(row);
if (row.WStatus == StatusEnum.写入失败)
{
_breakpointIndex = i;
OnPropertyChanged(nameof(BreakpointIndex));
OnPropertyChanged(nameof(HasBreakpoint));
hasNewFailure = true;
break;
}
}
// 全部成功(或从断点后全部成功)→ 重置断点
if (!hasNewFailure && _breakpointIndex >= 0)
{
_breakpointIndex = -1;
OnPropertyChanged(nameof(BreakpointIndex));
OnPropertyChanged(nameof(HasBreakpoint));
}
}
private async Task ReadRowAsync(TableRowModel? row)
{
if (row == null || CurrentTab == null) return;
await CurrentTab.ReadRow(row);
}
private async Task WriteRowAsync(TableRowModel? row)
{
if (row == null || CurrentTab == null) return;
await CurrentTab.WriteRow(row);
}
private void DeleteRow(TableRowModel? row)
{
CurrentTab?.DeleteRow(row!);
}
private void MoveUp(TableRowModel? row)
{
CurrentTab?.MoveUp(row!);
}
private void MoveDown(TableRowModel? row)
{
CurrentTab?.MoveDown(row!);
}
private void MoveTop(TableRowModel? row)
{
CurrentTab?.MoveToTop(row!);
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string? name = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
// 简单命令(无参数)
public class RelayCommand : ICommand
{
private readonly Action _act;
private readonly Func<bool>? _can;
public RelayCommand(Action act, Func<bool>? can = null) { _act = act; _can = can; }
public bool CanExecute(object? p) => _can?.Invoke() ?? true;
public void Execute(object? p) => _act();
public event EventHandler? CanExecuteChanged;
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
// 简单命令(带参数)
public class RelayCommand<T> : ICommand
{
private readonly Action<T?> _act;
public RelayCommand(Action<T?> act) { _act = act; }
public bool CanExecute(object? p) => true;
public void Execute(object? p) => _act((T?)p);
#pragma warning disable CS0067
public event EventHandler? CanExecuteChanged;
#pragma warning restore CS0067
}
}