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 属性 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 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 _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(); 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; _globalInfo.CurrentScope = TestStatus; _eventAggregator.GetEvent().Publish(TestStatus); } #endregion #region 重写 public override void OnNavigatedTo(NavigationContext navigationContext) { base.OnNavigatedTo(navigationContext); if (!IsInitiated&&navigationContext.Parameters.ContainsKey("Name")) { TestStatus = navigationContext.Parameters.GetValue("Name"); _scope = _globalInfo.ScopeDic[TestStatus]; _scopedContext = _scope.Resolve(); IsInitiated = true; } LoadTables(); } #endregion } }