356 lines
12 KiB
C#
356 lines
12 KiB
C#
using Logger;
|
||
using SqlSugar;
|
||
using System.Collections.ObjectModel;
|
||
using System.Data;
|
||
using System.IO;
|
||
using System.Text;
|
||
using System.Windows.Input;
|
||
using UIShare.GlobalVariable;
|
||
using UIShare.PubEvent;
|
||
using UIShare.ViewModelBase;
|
||
|
||
namespace MonitorModule.ViewModels
|
||
{
|
||
/// <summary>
|
||
/// 记录界面:用于查看运行目录下 SQL/ADP.db 中的数据。
|
||
/// 功能:表选择 / 关键字 WHERE 查询 / 分页 / 导出 CSV。
|
||
/// 仍延续 ScopedContext 隔离 + 双击展开 的范式,每个工位独立一份查询状态。
|
||
/// </summary>
|
||
public class RecordViewModel : NavigateViewModelBase, IRegionMemberLifetime, IDisposable
|
||
{
|
||
|
||
#region 属性
|
||
public bool KeepAlive => true;
|
||
public ScopedContext _scopedContext { get; set; }
|
||
public GlobalInfo _globalInfo { get; }
|
||
|
||
public string TestStatus
|
||
{
|
||
get => _testStatus;
|
||
set => SetProperty(ref _testStatus, value);
|
||
}
|
||
|
||
public ObservableCollection<string> TableNames
|
||
{
|
||
get => _tableNames;
|
||
set => SetProperty(ref _tableNames, value);
|
||
}
|
||
|
||
public string? SelectedTable
|
||
{
|
||
get => _selectedTable;
|
||
set
|
||
{
|
||
if (SetProperty(ref _selectedTable, value))
|
||
{
|
||
PageIndex = 1;
|
||
Query();
|
||
}
|
||
}
|
||
}
|
||
|
||
// 用户填写的 WHERE 子句(不带 WHERE 关键字),例如:Status='OK' AND Id>10
|
||
public string WhereClause
|
||
{
|
||
get => _whereClause;
|
||
set => SetProperty(ref _whereClause, value);
|
||
}
|
||
|
||
public DataTable ResultTable
|
||
{
|
||
get => _resultTable;
|
||
set => SetProperty(ref _resultTable, value);
|
||
}
|
||
|
||
public string StatusMessage
|
||
{
|
||
get => _statusMessage;
|
||
set => SetProperty(ref _statusMessage, value);
|
||
}
|
||
|
||
public int PageIndex
|
||
{
|
||
get => _pageIndex;
|
||
set => SetProperty(ref _pageIndex, value);
|
||
}
|
||
|
||
public int PageSize
|
||
{
|
||
get => _pageSize;
|
||
set
|
||
{
|
||
if (SetProperty(ref _pageSize, value <= 0 ? 50 : value))
|
||
{
|
||
RaisePropertyChanged(nameof(TotalPages));
|
||
PageIndex = 1;
|
||
Query();
|
||
}
|
||
}
|
||
}
|
||
|
||
public long TotalCount
|
||
{
|
||
get => _totalCount;
|
||
set
|
||
{
|
||
SetProperty(ref _totalCount, value);
|
||
RaisePropertyChanged(nameof(TotalPages));
|
||
}
|
||
}
|
||
|
||
public int TotalPages
|
||
{
|
||
get
|
||
{
|
||
if (PageSize <= 0) return 1;
|
||
var pages = (int)((TotalCount + PageSize - 1) / PageSize);
|
||
return pages <= 0 ? 1 : pages;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 命令
|
||
public ICommand LoadedCommand { get; }
|
||
public ICommand RefreshTablesCommand { get; }
|
||
public ICommand QueryCommand { get; }
|
||
public ICommand FirstPageCommand { get; }
|
||
public ICommand PrevPageCommand { get; }
|
||
public ICommand NextPageCommand { get; }
|
||
public ICommand LastPageCommand { get; }
|
||
public ICommand ExportCsvCommand { get; }
|
||
// 双击展开/折叠:与 MonitorView/AutomatedTestingView 共用
|
||
public ICommand RefreshCommand { get; }
|
||
#endregion
|
||
#region 私有字段
|
||
// 数据库相对路径:运行目录\SQL\ADP.db(数据库未就绪时容错处理,不抛异常)
|
||
private static readonly string DbFolder = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SQL");
|
||
private static readonly string DbPath = Path.Combine(DbFolder, "ADP.db");
|
||
private static readonly string ConnStr = $"Data Source={DbPath};Version=3;";
|
||
private bool IsInitiated = false;
|
||
private IScopedProvider _scope;
|
||
private string _testStatus = string.Empty;
|
||
private ObservableCollection<string> _tableNames = new();
|
||
private string? _selectedTable;
|
||
private string _whereClause = string.Empty;
|
||
private DataTable _resultTable = new();
|
||
private string _statusMessage = "未连接";
|
||
private int _pageIndex = 1;
|
||
private int _pageSize = 50;
|
||
private long _totalCount;
|
||
#endregion
|
||
|
||
public RecordViewModel(IContainerExtension container) : base(container)
|
||
{
|
||
_globalInfo = container.Resolve<GlobalInfo>();
|
||
LoadedCommand = new DelegateCommand(LoadTables);
|
||
RefreshTablesCommand = new DelegateCommand(LoadTables);
|
||
QueryCommand = new DelegateCommand(() => { PageIndex = 1; Query(); });
|
||
FirstPageCommand = new DelegateCommand(() => { if (PageIndex > 1) { PageIndex = 1; Query(); } });
|
||
PrevPageCommand = new DelegateCommand(() => { if (PageIndex > 1) { PageIndex--; Query(); } });
|
||
NextPageCommand = new DelegateCommand(() => { if (PageIndex < TotalPages) { PageIndex++; Query(); } });
|
||
LastPageCommand = new DelegateCommand(() => { if (PageIndex < TotalPages) { PageIndex = TotalPages; Query(); } });
|
||
ExportCsvCommand = new DelegateCommand(ExportCsv);
|
||
RefreshCommand = new DelegateCommand(OnExpand);
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_scope?.Dispose();
|
||
}
|
||
|
||
#region 数据库操作
|
||
private SqlSugarClient CreateClient()
|
||
{
|
||
return new SqlSugarClient(new ConnectionConfig
|
||
{
|
||
DbType = SqlSugar.DbType.Sqlite,
|
||
ConnectionString = ConnStr,
|
||
IsAutoCloseConnection = true,
|
||
InitKeyType = InitKeyType.Attribute
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
/// 读取数据库中的所有用户表。数据库不存在时不报错,仅在状态栏提示。
|
||
/// </summary>
|
||
private void LoadTables()
|
||
{
|
||
try
|
||
{
|
||
if (!Directory.Exists(DbFolder)) Directory.CreateDirectory(DbFolder);
|
||
if (!File.Exists(DbPath))
|
||
{
|
||
TableNames = new ObservableCollection<string>();
|
||
SelectedTable = null;
|
||
ResultTable = new DataTable();
|
||
TotalCount = 0;
|
||
StatusMessage = $"数据库尚未创建:{DbPath}";
|
||
return;
|
||
}
|
||
|
||
using var db = CreateClient();
|
||
var tables = db.DbMaintenance.GetTableInfoList(false)
|
||
.Select(t => t.Name)
|
||
.OrderBy(n => n)
|
||
.ToList();
|
||
|
||
TableNames = new ObservableCollection<string>(tables);
|
||
if (tables.Count == 0)
|
||
{
|
||
SelectedTable = null;
|
||
ResultTable = new DataTable();
|
||
TotalCount = 0;
|
||
StatusMessage = "数据库已连接,但暂无数据表";
|
||
}
|
||
else
|
||
{
|
||
StatusMessage = $"已连接:{DbPath}({tables.Count} 张表)";
|
||
if (string.IsNullOrEmpty(SelectedTable) || !tables.Contains(SelectedTable))
|
||
{
|
||
SelectedTable = tables[0]; // setter 会触发 Query
|
||
}
|
||
else
|
||
{
|
||
Query();
|
||
}
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoggerHelper.ErrorWithNotify($"加载数据表失败:{ex.Message}");
|
||
StatusMessage = $"加载失败:{ex.Message}";
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 执行分页查询。WHERE 为空则查询全部。
|
||
/// </summary>
|
||
private void Query()
|
||
{
|
||
if (string.IsNullOrEmpty(SelectedTable) || !File.Exists(DbPath))
|
||
{
|
||
ResultTable = new DataTable();
|
||
TotalCount = 0;
|
||
return;
|
||
}
|
||
|
||
try
|
||
{
|
||
using var db = CreateClient();
|
||
string table = $"[{SelectedTable}]";
|
||
string where = string.IsNullOrWhiteSpace(WhereClause) ? "1=1" : WhereClause;
|
||
|
||
// COUNT 总数
|
||
var countSql = $"SELECT COUNT(*) FROM {table} WHERE {where}";
|
||
TotalCount = db.Ado.GetLong(countSql);
|
||
|
||
// 修正越界
|
||
if (PageIndex > TotalPages) PageIndex = TotalPages;
|
||
if (PageIndex < 1) PageIndex = 1;
|
||
|
||
int offset = (PageIndex - 1) * PageSize;
|
||
var dataSql = $"SELECT * FROM {table} WHERE {where} LIMIT {PageSize} OFFSET {offset}";
|
||
ResultTable = db.Ado.GetDataTable(dataSql);
|
||
|
||
StatusMessage = $"表 [{SelectedTable}] 共 {TotalCount} 行 第 {PageIndex}/{TotalPages} 页";
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoggerHelper.ErrorWithNotify($"查询失败:{ex.Message}");
|
||
StatusMessage = $"查询失败:{ex.Message}";
|
||
ResultTable = new DataTable();
|
||
TotalCount = 0;
|
||
}
|
||
}
|
||
#endregion
|
||
|
||
#region 导出 CSV
|
||
/// <summary>
|
||
/// 按当前 WHERE 条件导出全部结果到 CSV(不分页)。
|
||
/// </summary>
|
||
private void ExportCsv()
|
||
{
|
||
if (string.IsNullOrEmpty(SelectedTable) || !File.Exists(DbPath))
|
||
{
|
||
StatusMessage = "无可导出数据";
|
||
return;
|
||
}
|
||
|
||
var dlg = new Microsoft.Win32.SaveFileDialog
|
||
{
|
||
Filter = "CSV 文件 (*.csv)|*.csv|所有文件|*.*",
|
||
FileName = $"{SelectedTable}_{DateTime.Now:yyyyMMdd_HHmmss}.csv",
|
||
Title = $"导出 [{SelectedTable}] 为 CSV"
|
||
};
|
||
if (dlg.ShowDialog() != true) return;
|
||
|
||
try
|
||
{
|
||
using var db = CreateClient();
|
||
string table = $"[{SelectedTable}]";
|
||
string where = string.IsNullOrWhiteSpace(WhereClause) ? "1=1" : WhereClause;
|
||
var sql = $"SELECT * FROM {table} WHERE {where}";
|
||
var dt = db.Ado.GetDataTable(sql);
|
||
|
||
WriteCsv(dlg.FileName, dt);
|
||
|
||
StatusMessage = $"导出完成:{dlg.FileName}({dt.Rows.Count} 行)";
|
||
LoggerHelper.InfoWithNotify($"工位 [{TestStatus}] 导出 [{SelectedTable}] 至 {dlg.FileName},共 {dt.Rows.Count} 行");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
LoggerHelper.ErrorWithNotify($"导出失败:{ex.Message}");
|
||
StatusMessage = $"导出失败:{ex.Message}";
|
||
}
|
||
}
|
||
|
||
private static void WriteCsv(string path, DataTable dt)
|
||
{
|
||
// 写 UTF-8 BOM 让 Excel 直接识别中文
|
||
using var sw = new StreamWriter(path, false, new UTF8Encoding(true));
|
||
// 表头
|
||
sw.WriteLine(string.Join(",", dt.Columns.Cast<DataColumn>().Select(c => Escape(c.ColumnName))));
|
||
// 行
|
||
foreach (DataRow row in dt.Rows)
|
||
{
|
||
sw.WriteLine(string.Join(",", row.ItemArray.Select(v => Escape(v?.ToString() ?? string.Empty))));
|
||
}
|
||
}
|
||
|
||
private static string Escape(string field)
|
||
{
|
||
if (field.Contains('"') || field.Contains(',') || field.Contains('\r') || field.Contains('\n'))
|
||
{
|
||
return "\"" + field.Replace("\"", "\"\"") + "\"";
|
||
}
|
||
return field;
|
||
}
|
||
#endregion
|
||
|
||
#region 双击展开
|
||
private void OnExpand()
|
||
{
|
||
if (string.IsNullOrEmpty(TestStatus)) return;
|
||
_globalInfo.CurrentScope = TestStatus;
|
||
_eventAggregator.GetEvent<ExpandViewEvent>().Publish(TestStatus);
|
||
|
||
}
|
||
#endregion
|
||
|
||
#region 重写
|
||
public override void OnNavigatedTo(NavigationContext navigationContext)
|
||
{
|
||
base.OnNavigatedTo(navigationContext);
|
||
if (!IsInitiated&&navigationContext.Parameters.ContainsKey("Name"))
|
||
{
|
||
TestStatus = navigationContext.Parameters.GetValue<string>("Name");
|
||
_scope = _globalInfo.ScopeDic[TestStatus];
|
||
_scopedContext = _scope.Resolve<ScopedContext>();
|
||
IsInitiated = true;
|
||
}
|
||
LoadTables();
|
||
}
|
||
#endregion
|
||
}
|
||
}
|