Initial import of WPF migration tool
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user