Files
FATrace/FATrace.WPLApp/ViewModels/RawProInputViewModel.cs
2025-10-29 11:42:58 +08:00

359 lines
15 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 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
{
/// <summary>
/// 原料入库信息查询 VM
/// - 枚举过滤RawSource、RawSplitState
/// - 条件:原料编号/名称、批号、时间范围CreateTime
/// - 支持分页、Excel 导出
/// </summary>
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<RawProInputDto>();
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;
/// <summary>
/// 原料来源(可空)
/// </summary>
public RawSource? SelectedRawSource { get => _selectedRawSource; set { _selectedRawSource = value; RaisePropertyChanged(); } }
private RawSource? _selectedRawSource;
/// <summary>
/// 分拆状态(可空)
/// </summary>
public RawSplitState? SelectedRawState { get => _selectedRawState; set { _selectedRawState = value; RaisePropertyChanged(); } }
private RawSplitState? _selectedRawState;
/// <summary>
/// 可绑定到下拉的枚举值列表
/// </summary>
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
/// <summary>
/// 查询结果数据集合DTO
/// </summary>
public ObservableCollection<RawProInputDto> 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
/// <summary>
/// 清空条件
/// </summary>
private void ClearFilters()
{
RawCode = RawName = Batch = string.Empty;
SelectedRawSource = null;
SelectedRawState = null;
StartTime = DateTime.Today;
EndTime = DateTime.Today.AddDays(1).AddSeconds(-1);
}
/// <summary>
/// 查询逻辑支持枚举过滤、时间范围CreateTime、分页、按 CreateTime 倒序
/// </summary>
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<RawProInput>();
// 时间范围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;
}
}
/// <summary>
/// 导出当前页结果为 Excel.xlsx
/// </summary>
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();
}
}
}