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

607 lines
22 KiB
C#
Raw Normal View History

using HandyControl.Controls;
using HandyControl.Tools.Extension;
using System.Collections.ObjectModel;
using YY.Admin.Core;
using YY.Admin.Core.Entity;
using YY.Admin.Core.Services;
using YY.Admin.Services.Service;
using YY.Admin.Views.RawMaterialEntry;
namespace YY.Admin.ViewModels.RawMaterialEntry;
public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResultable<bool>
{
private readonly IRawMaterialEntryService _entryService;
private readonly IJeecgDictSyncService _dictSyncService;
private readonly IMixerMaterialService _mixerMaterialService;
// 加载完物料后用于回填 Edit 模式选中项
private string? _pendingMaterialId;
private MesXslRawMaterialEntry? _entry;
public MesXslRawMaterialEntry? Entry
{
get => _entry;
set => SetProperty(ref _entry, value);
}
private MesMixerMaterial? _selectedMaterial;
public MesMixerMaterial? SelectedMaterial
{
get => _selectedMaterial;
set
{
if (!SetProperty(ref _selectedMaterial, value) || value == null || Entry == null) return;
Entry.MaterialId = value.Id;
Entry.MaterialCode = value.MaterialCode;
Entry.MaterialName = value.MaterialName;
Entry.ManufacturerMaterialName = value.AliasName;
RecalculateShelfLife(value);
RaisePropertyChanged(nameof(Entry));
RaisePropertyChanged(nameof(SelectedMaterialDisplay));
RaisePropertyChanged(nameof(HasSelectedMaterial));
// 新增模式自动生成条码/批次号
if (IsAddMode && !string.IsNullOrEmpty(value.MaterialCode))
_ = AutoGenerateBarcodeAsync(value.MaterialCode);
}
}
private bool _isGenerating;
public bool IsGenerating
{
get => _isGenerating;
set => SetProperty(ref _isGenerating, value);
}
public bool IsAddMode => string.IsNullOrWhiteSpace(Entry?.Id);
public string DialogTitle => IsAddMode ? "新增原料入场记录" : "编辑原料入场记录";
public string SelectedMaterialDisplay => _selectedMaterial == null
? "请选择密炼物料"
: $"[{_selectedMaterial.MaterialCode}] {_selectedMaterial.MaterialName}";
public bool HasSelectedMaterial => _selectedMaterial != null;
public string? IsSpecialAdoptionValue
{
get => Entry?.IsSpecialAdoption;
set
{
if (Entry == null || Entry.IsSpecialAdoption == value)
{
return;
}
Entry.IsSpecialAdoption = value;
if (!string.Equals(value, "1", StringComparison.Ordinal))
{
Entry.SpecialAdoptionOperator = null;
Entry.SpecialAdoptionTime = null;
Entry.SpecialAdoptionReason = null;
}
RaisePropertyChanged(nameof(Entry));
RaisePropertyChanged(nameof(IsSpecialAdoptionValue));
}
}
public double? TotalWeightInput
{
get => Entry?.TotalWeight;
set
{
if (Entry == null || Entry.TotalWeight == value) return;
Entry.TotalWeight = value;
RecalculatePortionWeight();
RaisePropertyChanged(nameof(Entry));
}
}
public int? TotalPortionsInput
{
get => Entry?.TotalPortions;
set
{
if (Entry == null || Entry.TotalPortions == value) return;
Entry.TotalPortions = value;
RecalculatePortionWeight();
RaisePropertyChanged(nameof(Entry));
}
}
public ObservableCollection<MesMixerMaterial> MaterialOptions { get; } = new();
public ObservableCollection<KeyValuePair<string, string>> TestResultOptions { get; } = new();
public ObservableCollection<KeyValuePair<string, string>> TestStatusOptions { get; } = new();
public ObservableCollection<KeyValuePair<string, string>> PrintFlagOptions { get; } = new();
public ObservableCollection<KeyValuePair<string, string>> StockBalanceOptions { get; } = new();
public ObservableCollection<KeyValuePair<string, string>> IsSpecialAdoptionOptions { get; } = new();
public ObservableCollection<KeyValuePair<string, string>> StatusOptions { get; } = new();
public ObservableCollection<RawMaterialSplitDetailItem> SplitCodeDetails { get; } = new();
public double SplitCodeTableHeight => CalculateSplitCodeTableHeight();
private bool _result;
public bool Result { get => _result; set => SetProperty(ref _result, value); }
public Action? CloseAction { get; set; }
public DelegateCommand SaveCommand { get; }
public DelegateCommand CancelCommand { get; }
public DelegateCommand ResetCommand { get; }
public DelegateCommand AddSplitDetailCommand { get; }
public DelegateCommand<RawMaterialSplitDetailItem> RemoveSplitDetailCommand { get; }
public DelegateCommand OpenMaterialPickerCommand { get; }
public DelegateCommand ClearMaterialCommand { get; }
public DelegateCommand OpenWeightRecordPickerCommand { get; }
public DelegateCommand ClearWeightRecordCommand { get; }
public RawMaterialEntryEditDialogViewModel(
IRawMaterialEntryService entryService,
IJeecgDictSyncService dictSyncService,
IMixerMaterialService mixerMaterialService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_entryService = entryService;
_dictSyncService = dictSyncService;
_mixerMaterialService = mixerMaterialService;
SaveCommand = new DelegateCommand(async () => await SaveAsync());
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
ResetCommand = new DelegateCommand(InitializeForAdd);
AddSplitDetailCommand = new DelegateCommand(AddSplitDetailRow);
RemoveSplitDetailCommand = new DelegateCommand<RawMaterialSplitDetailItem>(RemoveSplitDetailRow);
OpenMaterialPickerCommand = new DelegateCommand(async () => await OpenMaterialPickerAsync());
ClearMaterialCommand = new DelegateCommand(ClearMaterialSelection);
OpenWeightRecordPickerCommand = new DelegateCommand(async () => await OpenWeightRecordPickerAsync());
ClearWeightRecordCommand = new DelegateCommand(ClearWeightRecordSelection);
SplitCodeDetails.CollectionChanged += (_, _) => RaisePropertyChanged(nameof(SplitCodeTableHeight));
_ = LoadAllAsync();
}
private async Task LoadAllAsync()
{
await Task.WhenAll(LoadDictOptionsAsync(), LoadMaterialOptionsAsync());
}
private async Task LoadMaterialOptionsAsync()
{
try
{
// 每次打开弹窗都主动拉一次,确保直接写库的数据也能同步到
await _mixerMaterialService.SyncFromRemoteAsync();
var result = await _mixerMaterialService.PageAsync(1, 2000);
MaterialOptions.Clear();
foreach (var m in result.Records)
MaterialOptions.Add(m);
// Edit 模式:物料加载完后回填选中项
if (_pendingMaterialId != null)
{
var match = MaterialOptions.FirstOrDefault(m =>
string.Equals(m.Id, _pendingMaterialId, StringComparison.OrdinalIgnoreCase));
if (match != null)
SelectedMaterial = match;
_pendingMaterialId = null;
}
}
catch { /* 无法加载物料列表时不阻断表单 */ }
}
private async Task LoadDictOptionsAsync()
{
try
{
var testResultOpts = await _dictSyncService.GetDictOptionsAsync("xslmes_test_result");
var testStatusOpts = await _dictSyncService.GetDictOptionsAsync("xslmes_test_status");
var printFlagOpts = await _dictSyncService.GetDictOptionsAsync("xslmes_print_flag");
var ynOpts = await _dictSyncService.GetDictOptionsAsync("yn");
var statusOpts = await _dictSyncService.GetDictOptionsAsync("xslmes_entry_status");
PopulateOptions(TestResultOptions, testResultOpts, new[]
{
new KeyValuePair<string, string>("未检", "0"),
new KeyValuePair<string, string>("合格", "1"),
new KeyValuePair<string, string>("不合格", "2"),
});
PopulateOptions(TestStatusOptions, testStatusOpts, new[]
{
new KeyValuePair<string, string>("送样", "0"),
new KeyValuePair<string, string>("已批准", "1"),
});
PopulateOptions(PrintFlagOptions, printFlagOpts, new[]
{
new KeyValuePair<string, string>("未打印", "0"),
new KeyValuePair<string, string>("已打印", "1"),
});
PopulateOptions(StockBalanceOptions, ynOpts, new[]
{
new KeyValuePair<string, string>("否", "0"),
new KeyValuePair<string, string>("是", "1"),
});
PopulateOptions(IsSpecialAdoptionOptions, ynOpts, new[]
{
new KeyValuePair<string, string>("否", "0"),
new KeyValuePair<string, string>("是", "1"),
});
PopulateOptions(StatusOptions, statusOpts, Array.Empty<KeyValuePair<string, string>>());
ApplyDefaultEntryStatusForAdd();
}
catch
{
FillFallbackOptions();
ApplyDefaultEntryStatusForAdd();
}
}
private static void PopulateOptions(
ObservableCollection<KeyValuePair<string, string>> target,
IEnumerable<KeyValuePair<string, string>> items,
IEnumerable<KeyValuePair<string, string>> fallback)
{
target.Clear();
var list = items.ToList();
foreach (var item in list.Count > 0 ? list : fallback.ToList())
target.Add(item);
}
private void FillFallbackOptions()
{
PopulateOptions(TestResultOptions, Array.Empty<KeyValuePair<string, string>>(), new[]
{
new KeyValuePair<string, string>("未检", "0"),
new KeyValuePair<string, string>("合格", "1"),
new KeyValuePair<string, string>("不合格", "2"),
});
PopulateOptions(TestStatusOptions, Array.Empty<KeyValuePair<string, string>>(), new[]
{
new KeyValuePair<string, string>("送样", "0"),
new KeyValuePair<string, string>("已批准", "1"),
});
PopulateOptions(PrintFlagOptions, Array.Empty<KeyValuePair<string, string>>(), new[]
{
new KeyValuePair<string, string>("未打印", "0"),
new KeyValuePair<string, string>("已打印", "1"),
});
var ynDefault = new[] { new KeyValuePair<string, string>("否", "0"), new KeyValuePair<string, string>("是", "1") };
PopulateOptions(StockBalanceOptions, Array.Empty<KeyValuePair<string, string>>(), ynDefault);
PopulateOptions(IsSpecialAdoptionOptions, Array.Empty<KeyValuePair<string, string>>(), ynDefault);
}
private async Task AutoGenerateBarcodeAsync(string materialCode)
{
IsGenerating = true;
try
{
var code = await _entryService.GenerateBarcodeAsync(materialCode);
if (!string.IsNullOrEmpty(code) && Entry != null)
{
Entry.Barcode = code;
Entry.BatchNo = code;
RaisePropertyChanged(nameof(Entry));
}
}
finally { IsGenerating = false; }
}
public void InitializeForAdd()
{
_selectedMaterial = null;
RaisePropertyChanged(nameof(SelectedMaterial));
RaisePropertyChanged(nameof(SelectedMaterialDisplay));
RaisePropertyChanged(nameof(HasSelectedMaterial));
Entry = new MesXslRawMaterialEntry
{
EntryTime = DateTime.Now,
IsSpecialAdoption = "0"
};
InitializeSplitCodeDetailsFromEntry();
ApplyDefaultEntryStatusForAdd();
RaisePropertyChanged(nameof(IsAddMode));
RaisePropertyChanged(nameof(DialogTitle));
RaisePropertyChanged(nameof(TotalWeightInput));
RaisePropertyChanged(nameof(TotalPortionsInput));
RaisePropertyChanged(nameof(IsSpecialAdoptionValue));
}
public void InitializeForEdit(MesXslRawMaterialEntry entry)
{
Entry = new MesXslRawMaterialEntry
{
Id = entry.Id, Barcode = entry.Barcode, BatchNo = entry.BatchNo, EntryTime = entry.EntryTime,
WeightRecordId = entry.WeightRecordId, BillNo = entry.BillNo,
MaterialId = entry.MaterialId, MaterialCode = entry.MaterialCode, MaterialName = entry.MaterialName,
SupplyCustomer = entry.SupplyCustomer, SupplierId = entry.SupplierId, SupplierName = entry.SupplierName,
ManufacturerMaterialName = entry.ManufacturerMaterialName,
ShelfLife = entry.ShelfLife, TotalWeight = entry.TotalWeight, TotalPortions = entry.TotalPortions,
PortionWeight = entry.PortionWeight, PortionPackages = entry.PortionPackages,
TestResult = entry.TestResult, TestStatus = entry.TestStatus, PrintFlag = entry.PrintFlag,
StockBalance = entry.StockBalance, WarehouseLocation = entry.WarehouseLocation,
UnloadOperator = entry.UnloadOperator, IsSpecialAdoption = entry.IsSpecialAdoption,
SpecialAdoptionOperator = entry.SpecialAdoptionOperator, SpecialAdoptionTime = entry.SpecialAdoptionTime,
SpecialAdoptionReason = entry.SpecialAdoptionReason, Status = entry.Status, Remark = entry.Remark,
TenantId = entry.TenantId,
};
InitializeSplitCodeDetailsFromEntry();
// 若物料列表已加载则直接回填,否则记录 pending 等加载完后回填
if (MaterialOptions.Count > 0)
{
_selectedMaterial = MaterialOptions.FirstOrDefault(m =>
string.Equals(m.Id, entry.MaterialId, StringComparison.OrdinalIgnoreCase));
RaisePropertyChanged(nameof(SelectedMaterial));
RaisePropertyChanged(nameof(SelectedMaterialDisplay));
RaisePropertyChanged(nameof(HasSelectedMaterial));
}
else
{
_pendingMaterialId = entry.MaterialId;
}
RaisePropertyChanged(nameof(IsAddMode));
RaisePropertyChanged(nameof(DialogTitle));
RaisePropertyChanged(nameof(TotalWeightInput));
RaisePropertyChanged(nameof(TotalPortionsInput));
RaisePropertyChanged(nameof(IsSpecialAdoptionValue));
}
private async Task SaveAsync()
{
if (Entry == null) return;
try
{
ApplyFirstSplitDetailToEntry();
RecalculatePortionWeight();
bool ok;
if (IsAddMode)
{
ok = await _entryService.AddAsync(Entry);
if (ok) HandyControl.Controls.MessageBox.Success("新增成功!");
else { HandyControl.Controls.MessageBox.Error("新增失败!"); return; }
}
else
{
ok = await _entryService.EditAsync(Entry);
if (!ok) { HandyControl.Controls.MessageBox.Error("编辑失败!"); return; }
}
Result = ok;
if (IsAddMode && CloseAction == null)
{
// 独立新增页面:保存成功后自动清空表单,便于连续录入
InitializeForAdd();
return;
}
CloseAction?.Invoke();
}
catch (Exception ex)
{
HandyControl.Controls.MessageBox.Error($"操作失败:{ex.Message}");
}
}
private async Task OpenWeightRecordPickerAsync()
{
WeightRecordPickerDialogViewModel? pickerVm = null;
bool confirmed;
try
{
confirmed = await HandyControl.Controls.Dialog.Show<WeightRecordPickerDialogView>()
.Initialize<WeightRecordPickerDialogViewModel>(vm =>
{
pickerVm = vm;
vm.Initialize(Entry?.BillNo);
})
.GetResultAsync<bool>();
}
catch
{
return;
}
if (!confirmed || pickerVm?.SelectedRecord == null || Entry == null)
{
return;
}
var selected = pickerVm.SelectedRecord;
Entry.WeightRecordId = selected.Id;
Entry.BillNo = selected.BillNo;
Entry.SupplierName = selected.SenderUnit;
Entry.SupplierId = null;
RaisePropertyChanged(nameof(Entry));
}
private async Task OpenMaterialPickerAsync()
{
RawMaterialEntryMaterialPickerDialogViewModel? pickerVm = null;
bool confirmed;
try
{
confirmed = await HandyControl.Controls.Dialog.Show<RawMaterialEntryMaterialPickerDialogView>()
.Initialize<RawMaterialEntryMaterialPickerDialogViewModel>(vm =>
{
pickerVm = vm;
vm.Initialize(Entry?.MaterialCode, Entry?.MaterialName);
})
.GetResultAsync<bool>();
}
catch
{
return;
}
if (!confirmed || pickerVm?.SelectedMaterial == null)
{
return;
}
SelectedMaterial = pickerVm.SelectedMaterial;
}
private void ClearMaterialSelection()
{
_selectedMaterial = null;
RaisePropertyChanged(nameof(SelectedMaterial));
RaisePropertyChanged(nameof(SelectedMaterialDisplay));
RaisePropertyChanged(nameof(HasSelectedMaterial));
if (Entry == null)
{
return;
}
Entry.MaterialId = null;
Entry.MaterialCode = null;
Entry.MaterialName = null;
Entry.ManufacturerMaterialName = null;
Entry.ShelfLife = null;
RaisePropertyChanged(nameof(Entry));
}
private void ClearWeightRecordSelection()
{
if (Entry == null)
{
return;
}
Entry.WeightRecordId = null;
Entry.BillNo = null;
Entry.SupplierId = null;
Entry.SupplierName = null;
RaisePropertyChanged(nameof(Entry));
}
private void RecalculateShelfLife(MesMixerMaterial? material)
{
if (Entry == null || material?.ShelfLifeDays == null || material.ShelfLifeDays <= 0)
{
return;
}
Entry.ShelfLife = DateTime.Now.Date.AddDays(material.ShelfLifeDays.Value).ToString("yyyy-MM-dd");
}
private void RecalculatePortionWeight()
{
if (Entry == null)
{
return;
}
if (Entry.TotalWeight.HasValue && Entry.TotalPortions.HasValue && Entry.TotalPortions.Value > 0)
{
Entry.PortionWeight = Math.Round(Entry.TotalWeight.Value / Entry.TotalPortions.Value, 2, MidpointRounding.AwayFromZero);
return;
}
Entry.PortionWeight = null;
}
private void ApplyDefaultEntryStatusForAdd()
{
if (!IsAddMode || Entry == null || !string.IsNullOrWhiteSpace(Entry.Status))
{
return;
}
var pending = StatusOptions.FirstOrDefault(x =>
string.Equals(x.Key?.Trim(), "待处理", StringComparison.OrdinalIgnoreCase));
if (!string.IsNullOrWhiteSpace(pending.Value))
{
Entry.Status = pending.Value;
return;
}
// 字典未就绪时使用常见默认值
Entry.Status = "0";
}
private void InitializeSplitCodeDetailsFromEntry()
{
SplitCodeDetails.Clear();
SplitCodeDetails.Add(new RawMaterialSplitDetailItem
{
Portions = Entry?.TotalPortions,
PortionWeight = Entry?.PortionWeight,
PortionPackages = Entry?.PortionPackages,
WarehouseLocation = Entry?.WarehouseLocation
});
RaisePropertyChanged(nameof(SplitCodeTableHeight));
}
private void AddSplitDetailRow()
{
SplitCodeDetails.Add(new RawMaterialSplitDetailItem());
RaisePropertyChanged(nameof(SplitCodeTableHeight));
}
private void RemoveSplitDetailRow(RawMaterialSplitDetailItem? item)
{
if (item == null)
{
return;
}
SplitCodeDetails.Remove(item);
if (SplitCodeDetails.Count == 0)
{
SplitCodeDetails.Add(new RawMaterialSplitDetailItem());
}
RaisePropertyChanged(nameof(SplitCodeTableHeight));
}
private void ApplyFirstSplitDetailToEntry()
{
if (Entry == null || SplitCodeDetails.Count == 0)
{
return;
}
var first = SplitCodeDetails[0];
Entry.TotalPortions = first.Portions;
Entry.PortionWeight = first.PortionWeight;
Entry.PortionPackages = first.PortionPackages;
Entry.WarehouseLocation = first.WarehouseLocation;
}
private double CalculateSplitCodeTableHeight()
{
const double headerHeight = 36d;
const double rowHeight = 36d;
const double framePadding = 16d;
const double minRows = 1d;
const double maxRows = 6d;
var rowCount = Math.Clamp(SplitCodeDetails.Count, (int)minRows, (int)maxRows);
return headerHeight + rowCount * rowHeight + framePadding;
}
}
public class RawMaterialSplitDetailItem : BindableBase
{
private int? _portions;
public int? Portions
{
get => _portions;
set => SetProperty(ref _portions, value);
}
private double? _portionWeight;
public double? PortionWeight
{
get => _portionWeight;
set => SetProperty(ref _portionWeight, value);
}
private int? _portionPackages;
public int? PortionPackages
{
get => _portionPackages;
set => SetProperty(ref _portionPackages, value);
}
private string? _warehouseLocation;
public string? WarehouseLocation
{
get => _warehouseLocation;
set => SetProperty(ref _warehouseLocation, value);
}
}