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
{
///
/// 记录界面:用于查看运行目录下 SQL/ADP.db 中的数据。
/// 功能:表选择 / 关键字 WHERE 查询 / 分页 / 导出 CSV。
/// 仍延续 ScopedContext 隔离 + 双击展开 的范式,每个工位独立一份查询状态。
///
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 _tableNames = new();
public ObservableCollection 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();
_scopedContext = _scope.Resolve();
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
});
}
///
/// 读取数据库中的所有用户表。数据库不存在时不报错,仅在状态栏提示。
///
private void LoadTables()
{
try
{
if (!Directory.Exists(DbFolder)) Directory.CreateDirectory(DbFolder);
if (!File.Exists(DbPath))
{
TableNames = new ObservableCollection();
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(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}";
}
}
///
/// 执行分页查询。WHERE 为空则查询全部。
///
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
///
/// 按当前 WHERE 条件导出全部结果到 CSV(不分页)。
///
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().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().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("Name");
}
// 进入界面时自动加载表
LoadTables();
}
#endregion
}
}