using FATrace.WPLApp.Core; using Prism.Commands; using System; using System.Collections.ObjectModel; using FreeSql; using FATrace.Model; using FATrace.WPLApp.Services; using FATrace.WPLApp.ModelDto; using System.Threading.Tasks; using System.Windows; using Microsoft.Win32; using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; // 为避免与 System.Windows.HorizontalAlignment/VerticalAlignment 冲突,添加别名 using HAlign = NPOI.SS.UserModel.HorizontalAlignment; using VAlign = NPOI.SS.UserModel.VerticalAlignment; namespace FATrace.WPLApp.ViewModels { /// /// 原料入库信息查询 VM /// - 枚举过滤:RawSource、RawSplitState /// - 条件:原料编号/名称、批号、时间范围(CreateTime) /// - 支持分页、Excel 导出 /// public class RawProInputViewModel : NavigationViewModel { private readonly IFreeSql _fsql; private readonly ILogService _log; public RawProInputViewModel(IFreeSql fsql, ILogService log) { _fsql = fsql; _log = log; Items = new ObservableCollection(); Items.CollectionChanged += (s, e) => { try { ExportCommand?.RaiseCanExecuteChanged(); } catch { } }; // 默认时间范围为今日 StartTime = DateTime.Today; EndTime = DateTime.Today.AddDays(1).AddSeconds(-1); SearchCommand = new DelegateCommand(async () => await SearchAsync(), () => !IsBusy) .ObservesProperty(() => IsBusy); ExportCommand = new DelegateCommand(ExportToExcel, () => !IsBusy && Items.Count > 0) .ObservesProperty(() => IsBusy); ClearCommand = new DelegateCommand(ClearFilters, () => !IsBusy) .ObservesProperty(() => IsBusy); // 分页命令 FirstPageCommand = new DelegateCommand(async () => { if (PageIndex == 1) return; PageIndex = 1; await SearchAsync(); }, () => !IsBusy && PageIndex > 1) .ObservesProperty(() => IsBusy).ObservesProperty(() => PageIndex); PrevPageCommand = new DelegateCommand(async () => { if (PageIndex <= 1) return; PageIndex -= 1; await SearchAsync(); }, () => !IsBusy && PageIndex > 1) .ObservesProperty(() => IsBusy).ObservesProperty(() => PageIndex); NextPageCommand = new DelegateCommand(async () => { if (PageIndex >= TotalPages) return; PageIndex += 1; await SearchAsync(); }, () => !IsBusy && PageIndex < TotalPages) .ObservesProperty(() => IsBusy).ObservesProperty(() => PageIndex).ObservesProperty(() => TotalPages); LastPageCommand = new DelegateCommand(async () => { if (TotalPages <= 0 || PageIndex == TotalPages) return; PageIndex = TotalPages; await SearchAsync(); }, () => !IsBusy && PageIndex < TotalPages) .ObservesProperty(() => IsBusy).ObservesProperty(() => PageIndex).ObservesProperty(() => TotalPages); } #region 查询条件属性 public string? RawCode { get => _rawCode; set { _rawCode = value; RaisePropertyChanged(); } } private string? _rawCode; public string? RawName { get => _rawName; set { _rawName = value; RaisePropertyChanged(); } } private string? _rawName; public string? Batch { get => _batch; set { _batch = value; RaisePropertyChanged(); } } private string? _batch; /// /// 原料来源(可空) /// public RawSource? SelectedRawSource { get => _selectedRawSource; set { _selectedRawSource = value; RaisePropertyChanged(); } } private RawSource? _selectedRawSource; /// /// 分拆状态(可空) /// public RawSplitState? SelectedRawState { get => _selectedRawState; set { _selectedRawState = value; RaisePropertyChanged(); } } private RawSplitState? _selectedRawState; /// /// 可绑定到下拉的枚举值列表 /// public Array RawSourceValues => Enum.GetValues(typeof(RawSource)); public Array RawSplitStateValues => Enum.GetValues(typeof(RawSplitState)); public DateTime? StartTime { get => _startTime; set { _startTime = value; RaisePropertyChanged(); } } private DateTime? _startTime; public DateTime? EndTime { get => _endTime; set { _endTime = value; RaisePropertyChanged(); } } private DateTime? _endTime; #endregion #region 列表与状态 /// /// 查询结果数据集合(DTO) /// public ObservableCollection Items { get; } public bool IsBusy { get => _isBusy; set { _isBusy = value; RaisePropertyChanged(); } } private bool _isBusy; public int TotalCount { get => _totalCount; set { _totalCount = value; RaisePropertyChanged(); } } private int _totalCount; public int PageIndex { get => _pageIndex; set { _pageIndex = value < 1 ? 1 : value; RaisePropertyChanged(); } } private int _pageIndex = 1; public int PageSize { get => _pageSize; set { var v = value <= 0 ? 20 : value; if (_pageSize != v) { _pageSize = v; RaisePropertyChanged(); PageIndex = 1; if (!IsBusy) _ = SearchAsync(); } } } private int _pageSize = 20; public int TotalPages { get => _totalPages; set { _totalPages = value; RaisePropertyChanged(); } } private int _totalPages; #endregion #region 命令 public DelegateCommand SearchCommand { get; } public DelegateCommand ExportCommand { get; } public DelegateCommand ClearCommand { get; } public DelegateCommand FirstPageCommand { get; } public DelegateCommand PrevPageCommand { get; } public DelegateCommand NextPageCommand { get; } public DelegateCommand LastPageCommand { get; } #endregion /// /// 清空条件 /// private void ClearFilters() { RawCode = RawName = Batch = string.Empty; SelectedRawSource = null; SelectedRawState = null; StartTime = DateTime.Today; EndTime = DateTime.Today.AddDays(1).AddSeconds(-1); } /// /// 查询逻辑:支持枚举过滤、时间范围(CreateTime)、分页、按 CreateTime 倒序 /// private async Task SearchAsync() { if (IsBusy) return; try { if (StartTime.HasValue && EndTime.HasValue && StartTime > EndTime) { MessageBox.Show("开始时间不能大于结束时间", "提示", MessageBoxButton.OK, MessageBoxImage.Warning); return; } IsBusy = true; _log.Info("RawProInput 查询开始"); var data = await Task.Run(() => { var q = _fsql.Select(); // 时间范围:CreateTime DateTime? start = StartTime; DateTime? end = EndTime; if (start.HasValue) q = q.Where(a => a.CreateTime >= start.Value); if (end.HasValue) { var endInclusive = end.Value; if (endInclusive.TimeOfDay == TimeSpan.Zero) endInclusive = endInclusive.Date.AddDays(1).AddTicks(-1); q = q.Where(a => a.CreateTime <= endInclusive); } if (!string.IsNullOrWhiteSpace(RawCode)) q = q.Where(a => a.RawCode.Contains(RawCode)); if (!string.IsNullOrWhiteSpace(RawName)) q = q.Where(a => a.RawName.Contains(RawName)); if (!string.IsNullOrWhiteSpace(Batch)) q = q.Where(a => a.Batch.Contains(Batch)); if (SelectedRawSource.HasValue) q = q.Where(a => a.RawSource == SelectedRawSource.Value); if (SelectedRawState.HasValue) q = q.Where(a => a.RawState == SelectedRawState.Value); // 倒序 q = q.OrderByDescending(a => a.CreateTime); // 分页 var page = PageIndex < 1 ? 1 : PageIndex; var size = PageSize <= 0 ? 20 : PageSize; var list = q.Count(out var total) .Page(page, size) .ToList(a => new RawProInputDto { Id = a.Id, RawCode = a.RawCode, RawName = a.RawName, Weight = a.Weight, Batch = a.Batch, ShelfLife = a.ShelfLife, RawSource = a.RawSource.ToString(), RemainWeight = a.RemainWeight, RawState = a.RawState.ToString(), CreateTime = a.CreateTime }); var pages = total <= 0 || size <= 0 ? 0 : (int)Math.Ceiling(total * 1.0 / size); if (pages > 0 && page > pages) { page = pages; list = q.Page(page, size).ToList(a => new RawProInputDto { Id = a.Id, RawCode = a.RawCode, RawName = a.RawName, Weight = a.Weight, Batch = a.Batch, ShelfLife = a.ShelfLife, RawSource = a.RawSource.ToString(), RemainWeight = a.RemainWeight, RawState = a.RawState.ToString(), CreateTime = a.CreateTime }); } return (items: list, total: (int)total, normalizedPage: page, totalPages: pages); }); Application.Current.Dispatcher.Invoke(() => { Items.Clear(); foreach (var it in data.items) Items.Add(it); TotalCount = data.total; TotalPages = data.totalPages; PageIndex = data.normalizedPage == 0 ? 1 : data.normalizedPage; }); _log.Info($"RawProInput 查询完成,记录数: {TotalCount}"); } catch (Exception ex) { _log.Error($"RawProInput 查询失败: {ex}"); MessageBox.Show($"查询失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } finally { IsBusy = false; } } /// /// 导出当前页结果为 Excel(.xlsx) /// private void ExportToExcel() { if (IsBusy) return; if (Items.Count == 0) { MessageBox.Show("无可导出的数据", "提示", MessageBoxButton.OK, MessageBoxImage.Information); return; } try { IsBusy = true; var sfd = new SaveFileDialog { Title = "导出为 Excel", Filter = "Excel 工作簿 (*.xlsx)|*.xlsx", FileName = $"RawProInput_{DateTime.Now:yyyyMMdd_HHmmss}.xlsx" }; if (sfd.ShowDialog() != true) return; using IWorkbook workbook = new XSSFWorkbook(); var sheet = workbook.CreateSheet("原料入库"); // 表头 var header = sheet.CreateRow(0); string[] headers = new[] { "Id","原料编号","原料名称","入库重量(kg)","批号","保质期(年)","原料来源","剩余重量(kg)","分拆状态","创建时间" }; var headerFont = workbook.CreateFont(); headerFont.IsBold = true; headerFont.FontHeightInPoints = 12; var headerStyle = workbook.CreateCellStyle(); headerStyle.SetFont(headerFont); headerStyle.Alignment = HAlign.Center; headerStyle.VerticalAlignment = VAlign.Center; header.HeightInPoints = 20f; for (int i = 0; i < headers.Length; i++) { var hc = header.CreateCell(i); hc.SetCellValue(headers[i]); hc.CellStyle = headerStyle; } // 日期格式 var dateStyle = workbook.CreateCellStyle(); var dataFormat = workbook.CreateDataFormat(); dateStyle.DataFormat = dataFormat.GetFormat("yyyy-mm-dd hh:mm:ss"); // 数据 for (int r = 0; r < Items.Count; r++) { var it = Items[r]; var row = sheet.CreateRow(r + 1); int c = 0; row.CreateCell(c++).SetCellValue((double)it.Id); row.CreateCell(c++).SetCellValue(it.RawCode ?? string.Empty); row.CreateCell(c++).SetCellValue(it.RawName ?? string.Empty); row.CreateCell(c++).SetCellValue(it.Weight); row.CreateCell(c++).SetCellValue(it.Batch ?? string.Empty); row.CreateCell(c++).SetCellValue(it.ShelfLife); row.CreateCell(c++).SetCellValue(it.RawSource); row.CreateCell(c++).SetCellValue(it.RemainWeight); row.CreateCell(c++).SetCellValue(it.RawState); var cellCreateTime = row.CreateCell(c++); cellCreateTime.SetCellValue(it.CreateTime); cellCreateTime.CellStyle = dateStyle; } for (int i = 0; i < headers.Length; i++) sheet.AutoSizeColumn(i); using var fs = System.IO.File.OpenWrite(sfd.FileName); workbook.Write(fs); _log.Info($"RawProInput 导出完成: {sfd.FileName}"); MessageBox.Show("导出成功", "提示", MessageBoxButton.OK, MessageBoxImage.Information); } catch (Exception ex) { _log.Error($"RawProInput 导出失败: {ex}"); MessageBox.Show($"导出失败: {ex.Message}", "错误", MessageBoxButton.OK, MessageBoxImage.Error); } finally { IsBusy = false; } } public override async void OnNavigatedTo(Prism.Regions.NavigationContext navigationContext) { await SearchAsync(); } } }