Files
henlo_migration_wpf10/henlo_migration_wpf10/ViewModels/MainViewModel.cs
T

531 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 TableFieldTab { get; }
public TabServices BosTab { get; }
public TabServices BosFieldTab { 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(TableFieldsData));
OnPropertyChanged(nameof(BosTablesData));
OnPropertyChanged(nameof(BosFieldsData));
OnPropertyChanged(nameof(StoredProcsData));
OnPropertyChanged(nameof(CurrentDataData));
}
}
// Tab 切换
private int _selectedTabIndex;
public int SelectedTabIndex
{
get => _selectedTabIndex;
set
{
_selectedTabIndex = value;
CurrentTab = value switch
{
0 => TableTab,
1 => TableFieldTab,
2 => BosTab,
3 => BosFieldTab,
4 => PrcTab,
5 => DataTab,
_ => TableTab
};
OnPropertyChanged();
OnPropertyChanged(nameof(TablesData));
OnPropertyChanged(nameof(TableFieldsData));
OnPropertyChanged(nameof(BosTablesData));
OnPropertyChanged(nameof(BosFieldsData));
OnPropertyChanged(nameof(StoredProcsData));
OnPropertyChanged(nameof(CurrentDataData));
OnPropertyChanged(nameof(CurrentTabName));
OnPropertyChanged(nameof(CurrentTabCount));
}
}
// 六个Tab数据源别名(供DataGrid绑定)
public ObservableCollection<TableRowModel> TablesData => TableTab.List;
public ObservableCollection<TableRowModel> TableFieldsData => TableFieldTab.List;
public ObservableCollection<TableRowModel> BosTablesData => BosTab.List;
public ObservableCollection<TableRowModel> BosFieldsData => BosFieldTab.List;
public ObservableCollection<TableRowModel> StoredProcsData => PrcTab.List;
public ObservableCollection<TableRowModel> CurrentDataData => DataTab.List;
public string CurrentTabName => SelectedTabIndex switch
{
0 => "表配置",
1 => "表字段",
2 => "BOS配置",
3 => "BOS字段配置",
4 => "存储过程",
5 => "表数据",
_ => ""
};
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 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));
TableFieldTab = new TabServices(new TabConfig(TabsEnum.表字段, CommonHelper.DataFromPath, CommonHelper.DataPath, "table_field.txt", CodeExt.table_field,
DbServices.GetTableField, (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));
BosFieldTab = new TabServices(new TabConfig(TabsEnum.BOS字段配置, CommonHelper.DataFromPath, CommonHelper.DataPath, "bos_field.txt", CodeExt.bos_field,
DbServices.GetTableBosField, (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, TableFieldTab, BosTab, BosFieldTab, 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);
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))
{
string itemName = InputText.Trim();
if (CurrentTab == TableFieldTab || CurrentTab == BosFieldTab)
{
if (!DbServices.TryParseTableFieldName(itemName, out string tableName, out string columnName))
{
string tabName = CurrentTab == TableFieldTab ? "表字段" : "BOS 字段配置";
HandyControl.Controls.Growl.Warning($"{tabName}必须使用 TABLE.COLUMN 格式");
return;
}
itemName = $"{tableName}.{columnName}";
}
CurrentTab?.AddRow(itemName.ToLowerInvariant());
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 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
}
}