Files
ADP/MonitorModule/ViewModels/RecordViewModel.cs
2026-06-05 10:57:09 +08:00

363 lines
13 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 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
// 数据库相对路径:运行目录\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 readonly IScopedProvider _scope;
#endregion
#region ScopedContext
public bool KeepAlive => true;
public ScopedContext _scopedContext { get; }
// 公开一份 GlobalInfo 供双击展开逻辑使用(基类的 _globalInfo 是 private
public GlobalInfo GlobalInfoRef { get; }
#endregion
#region
private string _testStatus = string.Empty;
public string TestStatus
{
get => _testStatus;
set => SetProperty(ref _testStatus, value);
}
#endregion
#region / /
private ObservableCollection<string> _tableNames = new();
public ObservableCollection<string> TableNames
{
get => _tableNames;
set => SetProperty(ref _tableNames, value);
}
private string? _selectedTable;
public string? SelectedTable
{
get => _selectedTable;
set
{
if (SetProperty(ref _selectedTable, value))
{
PageIndex = 1;
Query();
}
}
}
// 用户填写的 WHERE 子句(不带 WHERE 关键字例如Status='OK' AND Id>10
private string _whereClause = string.Empty;
public string WhereClause
{
get => _whereClause;
set => SetProperty(ref _whereClause, value);
}
private DataTable _resultTable = new();
public DataTable ResultTable
{
get => _resultTable;
set => SetProperty(ref _resultTable, value);
}
private string _statusMessage = "未连接";
public string StatusMessage
{
get => _statusMessage;
set => SetProperty(ref _statusMessage, value);
}
#endregion
#region
private int _pageIndex = 1;
public int PageIndex
{
get => _pageIndex;
set => SetProperty(ref _pageIndex, value);
}
private int _pageSize = 50;
public int PageSize
{
get => _pageSize;
set
{
if (SetProperty(ref _pageSize, value <= 0 ? 50 : value))
{
RaisePropertyChanged(nameof(TotalPages));
PageIndex = 1;
Query();
}
}
}
private long _totalCount;
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
public RecordViewModel(IContainerExtension container) : base(container)
{
_scope = container.CreateScope();
GlobalInfoRef = container.Resolve<GlobalInfo>();
_scopedContext = _scope.Resolve<ScopedContext>();
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;
_eventAggregator.GetEvent<ExpandViewEvent>().Publish(TestStatus);
GlobalInfoRef.CurrentScope = TestStatus;
}
#endregion
#region
public override void OnNavigatedTo(NavigationContext navigationContext)
{
base.OnNavigatedTo(navigationContext);
if (navigationContext.Parameters.ContainsKey("Name"))
{
TestStatus = navigationContext.Parameters.GetValue<string>("Name");
}
// 进入界面时自动加载表
LoadTables();
}
#endregion
}
}