Files
qhmes/yy-admin-master/YY.Admin/ViewModels/RawMaterialEntry/RawMaterialEntryOperationViewModel.cs

289 lines
11 KiB
C#
Raw Normal View History

using Prism.Commands;
using System.Collections.ObjectModel;
using System.IO;
using System.Text.Json;
using YY.Admin.Core.Entity;
using YY.Admin.Core.Services;
using YY.Admin.Services.Service;
namespace YY.Admin.ViewModels.RawMaterialEntry;
/// <summary>
/// 「新增原料入场记录」独立页面:左侧表单逻辑继承编辑 VM右侧展示当日入场简要列表并支持选中回填模板。
/// </summary>
public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogViewModel
{
private const int TodayListFetchSize = 5000;
private readonly IRawMaterialCardService _rawMaterialCardService;
private static readonly JsonSerializerOptions LayoutJsonOpts = new()
{
PropertyNameCaseInsensitive = true,
WriteIndented = true
};
private static string LayoutFilePath => Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
"YY.Admin",
"raw-material-entry-add-layout.json");
private bool _suppressTodaySelectionReaction;
public ObservableCollection<MesXslRawMaterialEntry> TodayEntries { get; } = new();
private MesXslRawMaterialEntry? _selectedTodayEntry;
public MesXslRawMaterialEntry? SelectedTodayEntry
{
get => _selectedTodayEntry;
set
{
if (!SetProperty(ref _selectedTodayEntry, value)) return;
if (_suppressTodaySelectionReaction || value == null) return;
_ = ApplyTodayRowToFormAsync(value);
}
}
private bool _isRightPanelExpanded = true;
public bool IsRightPanelExpanded
{
get => _isRightPanelExpanded;
set
{
if (!SetProperty(ref _isRightPanelExpanded, value)) return;
SaveLayoutState();
}
}
private double _expandedRightPanelWidth = 280;
/// <summary>右侧面板展开时的目标宽度(像素),由 GridSplitter 拖拽结束时回写。</summary>
public double ExpandedRightPanelWidth
{
get => _expandedRightPanelWidth;
private set
{
var v = Math.Clamp(value, 200, 560);
if (SetProperty(ref _expandedRightPanelWidth, v))
SaveLayoutState();
}
}
/// <summary>仅当入场记录已保存(有 Id且存在拆码明细时允许生成原材料卡片。</summary>
public bool CanGenerateCards => !string.IsNullOrWhiteSpace(Entry?.Id) && SplitCodeDetails.Count > 0;
public DelegateCommand ToggleRightPanelCommand { get; }
public DelegateCommand RefreshTodayEntriesCommand { get; }
public DelegateCommand GenerateRawMaterialCardsCommand { get; }
public RawMaterialEntryOperationViewModel(
IRawMaterialEntryService entryService,
IJeecgDictSyncService dictSyncService,
IMixerMaterialService mixerMaterialService,
IRawMaterialCardService rawMaterialCardService,
IContainerExtension container,
IRegionManager regionManager)
: base(entryService, dictSyncService, mixerMaterialService, container, regionManager)
{
_rawMaterialCardService = rawMaterialCardService;
LoadLayoutState();
ToggleRightPanelCommand = new DelegateCommand(() => IsRightPanelExpanded = !IsRightPanelExpanded);
RefreshTodayEntriesCommand = new DelegateCommand(async () => await LoadTodayEntriesAsync());
GenerateRawMaterialCardsCommand = new DelegateCommand(async () => await GenerateRawMaterialCardsAsync());
SplitCodeDetails.CollectionChanged += (_, _) => RaisePropertyChanged(nameof(CanGenerateCards));
}
public override void InitializeForAdd()
{
_suppressTodaySelectionReaction = true;
_selectedTodayEntry = null;
RaisePropertyChanged(nameof(SelectedTodayEntry));
_suppressTodaySelectionReaction = false;
base.InitializeForAdd();
RaisePropertyChanged(nameof(CanGenerateCards));
}
/// <summary>页面首次加载时拉取「今日」列表(由 View Loaded 调用)。</summary>
public async Task LoadTodayEntriesOnFirstShowAsync() => await LoadTodayEntriesAsync();
/// <summary>用户在拖拽结束 GridSplitter 后提交实际列宽。</summary>
public void CommitRightPanelWidthFromView(double actualWidth)
{
if (!IsRightPanelExpanded || actualWidth < 1) return;
ExpandedRightPanelWidth = actualWidth;
}
private async Task LoadTodayEntriesAsync()
{
try
{
IsLoading = true;
var result = await EntryService.PageAsync(1, TodayListFetchSize);
var today = DateTime.Today;
var rows = result.Records
.Where(e => IsTodayEntry(e, today))
.OrderByDescending(e => e.EntryTime ?? e.CreateTime ?? DateTime.MinValue)
.ToList();
TodayEntries.Clear();
foreach (var r in rows)
TodayEntries.Add(r);
}
catch
{
// 列表失败不阻断左侧新增(与物料加载策略一致)
}
finally
{
IsLoading = false;
}
}
private static bool IsTodayEntry(MesXslRawMaterialEntry e, DateTime today)
{
var byEntry = e.EntryTime?.Date == today;
var byCreate = e.CreateTime?.Date == today;
return byEntry || byCreate;
}
/// <summary>
/// 点击右侧今日记录:直接以「编辑模式」加载源记录到左侧表单(保留 Id/条码/批次号),
/// 不再走「新增模板」逻辑,避免重新生成条码、覆盖原数据。
/// </summary>
private async Task ApplyTodayRowToFormAsync(MesXslRawMaterialEntry src)
{
// 复用基类 InitializeForEdit保留 Id/Barcode/BatchNo/状态等所有字段,标题自动切到「编辑原料入场记录」
base.InitializeForEdit(src);
RaisePropertyChanged(nameof(CanGenerateCards));
// 若物料列表此前未加载导致选中项未回填,则补一次拉取(与编辑弹窗逻辑一致)
if (_pendingMaterialId != null)
await LoadMaterialOptionsAsync();
}
/// <summary>编辑保存成功后:刷新右侧今日列表并切回新增态,避免连续误改同一条。</summary>
protected override async Task SaveAsync()
{
var wasEdit = !IsAddMode;
await base.SaveAsync();
RaisePropertyChanged(nameof(CanGenerateCards));
if (wasEdit && Result && CloseAction == null)
{
HandyControl.Controls.MessageBox.Success("编辑成功!");
await LoadTodayEntriesAsync();
InitializeForAdd();
}
}
private async Task GenerateRawMaterialCardsAsync()
{
if (Entry == null || string.IsNullOrWhiteSpace(Entry.Id))
{
HandyControl.Controls.MessageBox.Warning("请先保存入场记录再生成原材料卡片!");
return;
}
if (SplitCodeDetails.Count == 0)
{
HandyControl.Controls.MessageBox.Warning("当前入场记录无拆分明细,无法生成原材料卡片!");
return;
}
try
{
IsLoading = true;
var globalIndex = 1;
var baseBarcode = Entry.Barcode ?? "";
var failCount = 0;
foreach (var detail in SplitCodeDetails)
{
var portions = detail.Portions ?? 0;
for (var i = 0; i < portions; i++)
{
var card = new MesXslRawMaterialCard
{
Barcode = baseBarcode + globalIndex.ToString("D3"),
BatchNo = Entry.BatchNo,
EntryDate = Entry.EntryTime?.Date ?? DateTime.Today,
MaterialId = Entry.MaterialId,
MaterialName = Entry.MaterialName,
SupplierId = Entry.SupplierId,
SupplierName = Entry.SupplierName,
ManufacturerMaterialName = Entry.ManufacturerMaterialName,
ShelfLife = Entry.ShelfLife,
TotalWeight = detail.PortionWeight.HasValue ? (decimal?)detail.PortionWeight.Value : null,
RemainingWeight = detail.PortionWeight.HasValue ? (decimal?)detail.PortionWeight.Value : null,
RemainingQuantity = detail.PortionPackages,
WarehouseArea = detail.WarehouseLocation,
UnloadOperator = Entry.UnloadOperator,
Status = "1",
TestResult = "0",
PriorityPickup = "0",
TenantId = Entry.TenantId
};
var ok = await _rawMaterialCardService.AddAsync(card);
if (!ok) failCount++;
globalIndex++;
}
}
// 更新打印状态为「已打印」
Entry.PrintFlag = "1";
await EntryService.EditAsync(Entry);
RaisePropertyChanged(nameof(Entry));
var total = globalIndex - 1;
if (failCount == 0)
HandyControl.Controls.MessageBox.Success($"已生成 {total} 张原材料卡片,打印状态已更新为「已打印」!");
else
HandyControl.Controls.MessageBox.Warning($"共生成 {total} 张,其中 {failCount} 张失败,请检查网络后重试!");
}
catch (Exception ex)
{
HandyControl.Controls.MessageBox.Error($"生成原材料卡片失败:{ex.Message}");
}
finally
{
IsLoading = false;
}
}
private void LoadLayoutState()
{
try
{
if (!File.Exists(LayoutFilePath)) return;
var json = File.ReadAllText(LayoutFilePath);
var dto = JsonSerializer.Deserialize<AddPageLayoutDto>(json, LayoutJsonOpts);
if (dto == null) return;
if (dto.ExpandedWidth is > 0 and < 2000)
_expandedRightPanelWidth = Math.Clamp(dto.ExpandedWidth.Value, 200, 560);
if (dto.IsExpanded.HasValue)
_isRightPanelExpanded = dto.IsExpanded.Value;
RaisePropertyChanged(nameof(ExpandedRightPanelWidth));
RaisePropertyChanged(nameof(IsRightPanelExpanded));
}
catch { /* 布局文件损坏时忽略 */ }
}
private void SaveLayoutState()
{
try
{
var dir = Path.GetDirectoryName(LayoutFilePath);
if (!string.IsNullOrEmpty(dir))
Directory.CreateDirectory(dir);
var dto = new AddPageLayoutDto
{
ExpandedWidth = _expandedRightPanelWidth,
IsExpanded = _isRightPanelExpanded
};
File.WriteAllText(LayoutFilePath, JsonSerializer.Serialize(dto, LayoutJsonOpts));
}
catch { /* 写本地失败时不影响业务 */ }
}
private sealed class AddPageLayoutDto
{
public double? ExpandedWidth { get; set; }
public bool? IsExpanded { get; set; }
}
}