新增MES库区管理功能,包含免密接口、数据处理逻辑及相关控制器、服务和实体的实现。支持库区的增删改查操作,优化用户体验并增强系统的实时数据同步能力。
This commit is contained in:
@@ -63,6 +63,9 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
|
||||
public bool IsAddMode => string.IsNullOrWhiteSpace(Entry?.Id);
|
||||
public string DialogTitle => IsAddMode ? "新增原料入场记录" : "编辑原料入场记录";
|
||||
/// <summary>已打印(PrintFlag=="1"):总重只读、不可新增明细。</summary>
|
||||
public bool IsPrinted => string.Equals(Entry?.PrintFlag, "1", StringComparison.Ordinal);
|
||||
public bool IsTotalWeightEditable => !IsPrinted;
|
||||
public string SelectedMaterialDisplay => _selectedMaterial == null
|
||||
? "请选择密炼物料"
|
||||
: $"[{_selectedMaterial.MaterialCode}] {_selectedMaterial.MaterialName}";
|
||||
@@ -130,6 +133,10 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
public DelegateCommand ClearWeightRecordCommand { get; }
|
||||
public DelegateCommand OpenSupplierPickerCommand { get; }
|
||||
public DelegateCommand ClearSupplierCommand { get; }
|
||||
/// <summary>
|
||||
/// 拆码明细 - 库位选择命令(弹出「库区选择」弹窗,单选)。CommandParameter 为当前行 RawMaterialSplitDetailItem。
|
||||
/// </summary>
|
||||
public DelegateCommand<RawMaterialSplitDetailItem> OpenWarehouseAreaPickerCommand { get; }
|
||||
|
||||
public RawMaterialEntryEditDialogViewModel(
|
||||
IRawMaterialEntryService entryService,
|
||||
@@ -144,7 +151,7 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
SaveCommand = new DelegateCommand(async () => await SaveAsync());
|
||||
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
|
||||
ResetCommand = new DelegateCommand(InitializeForAdd);
|
||||
AddSplitDetailCommand = new DelegateCommand(AddSplitDetailRow);
|
||||
AddSplitDetailCommand = new DelegateCommand(AddSplitDetailRow, () => !IsPrinted);
|
||||
RemoveSplitDetailCommand = new DelegateCommand<RawMaterialSplitDetailItem>(RemoveSplitDetailRow);
|
||||
OpenMaterialPickerCommand = new DelegateCommand(async () => await OpenMaterialPickerAsync());
|
||||
ClearMaterialCommand = new DelegateCommand(ClearMaterialSelection);
|
||||
@@ -152,6 +159,7 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
ClearWeightRecordCommand = new DelegateCommand(ClearWeightRecordSelection);
|
||||
OpenSupplierPickerCommand = new DelegateCommand(async () => await OpenSupplierPickerAsync());
|
||||
ClearSupplierCommand = new DelegateCommand(ClearSupplierSelection);
|
||||
OpenWarehouseAreaPickerCommand = new DelegateCommand<RawMaterialSplitDetailItem>(async row => await OpenWarehouseAreaPickerAsync(row));
|
||||
SplitCodeDetails.CollectionChanged += OnSplitCodeDetailsCollectionChanged;
|
||||
_ = LoadAllAsync();
|
||||
}
|
||||
@@ -304,6 +312,9 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
RaisePropertyChanged(nameof(SplitTotalPortionsDisplay));
|
||||
RaisePropertyChanged(nameof(SplitPortionWeightDisplay));
|
||||
RaisePropertyChanged(nameof(SplitPortionPackagesDisplay));
|
||||
RaisePropertyChanged(nameof(IsPrinted));
|
||||
RaisePropertyChanged(nameof(IsTotalWeightEditable));
|
||||
AddSplitDetailCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
public void InitializeForEdit(MesXslRawMaterialEntry entry)
|
||||
@@ -317,6 +328,9 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
ManufacturerMaterialName = entry.ManufacturerMaterialName,
|
||||
ShelfLife = entry.ShelfLife, TotalWeight = entry.TotalWeight, TotalPortions = entry.TotalPortions,
|
||||
PortionWeight = entry.PortionWeight, PortionPackages = entry.PortionPackages,
|
||||
PortionWarehouseLocations = entry.PortionWarehouseLocations,
|
||||
PortionDetailIds = entry.PortionDetailIds,
|
||||
PortionCardFlags = entry.PortionCardFlags,
|
||||
TestResult = entry.TestResult, TestStatus = entry.TestStatus, PrintFlag = entry.PrintFlag,
|
||||
StockBalance = entry.StockBalance, WarehouseLocation = entry.WarehouseLocation,
|
||||
UnloadOperator = entry.UnloadOperator, IsSpecialAdoption = entry.IsSpecialAdoption,
|
||||
@@ -347,6 +361,9 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
RaisePropertyChanged(nameof(SplitTotalPortionsDisplay));
|
||||
RaisePropertyChanged(nameof(SplitPortionWeightDisplay));
|
||||
RaisePropertyChanged(nameof(SplitPortionPackagesDisplay));
|
||||
RaisePropertyChanged(nameof(IsPrinted));
|
||||
RaisePropertyChanged(nameof(IsTotalWeightEditable));
|
||||
AddSplitDetailCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
protected virtual async Task SaveAsync()
|
||||
@@ -357,6 +374,18 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
ApplySplitDetailsToEntry();
|
||||
ApplyDefaultEntryStatusForAdd();
|
||||
ApplyHiddenFieldDefaultsForAdd();
|
||||
|
||||
var missing = new List<string>();
|
||||
if (string.IsNullOrWhiteSpace(Entry.MaterialId)) missing.Add("密炼物料");
|
||||
if (string.IsNullOrWhiteSpace(Entry.BillNo)) missing.Add("榜单号");
|
||||
if (string.IsNullOrWhiteSpace(Entry.UnloadOperator)) missing.Add("卸货人");
|
||||
if (string.IsNullOrWhiteSpace(Entry.SupplierName)) missing.Add("供应商名称");
|
||||
if (missing.Count > 0)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Warning($"以下必填项不能为空:{string.Join("、", missing)}");
|
||||
return;
|
||||
}
|
||||
|
||||
bool ok;
|
||||
if (IsAddMode)
|
||||
{
|
||||
@@ -414,7 +443,57 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
Entry.BillNo = selected.BillNo;
|
||||
Entry.SupplierName = selected.SenderUnit;
|
||||
Entry.SupplierId = null;
|
||||
// 选择榜单后,自动把「剩余可入场量 = 净重 - 已入场重量」带入「总重」,
|
||||
// 用户仍可手动编辑;若净重为空则保留原值,避免误清空。
|
||||
if (selected.NetWeight.HasValue)
|
||||
{
|
||||
var entered = selected.EnteredWeight ?? 0d;
|
||||
var remaining = selected.NetWeight.Value - entered;
|
||||
// 已入场重量可能因数据异常超过净重;UI 不展示负数,强制夹到 0。
|
||||
Entry.TotalWeight = Math.Round(Math.Max(0d, remaining), 2);
|
||||
}
|
||||
RaisePropertyChanged(nameof(Entry));
|
||||
RaisePropertyChanged(nameof(TotalWeightInput));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 弹出「库区选择」弹窗,把选中的 AreaName 写回当前明细行的 WarehouseLocation。
|
||||
/// 入参 row:被点击的拆码明细行;为 null 直接 return(防御性)。
|
||||
/// </summary>
|
||||
private async Task OpenWarehouseAreaPickerAsync(RawMaterialSplitDetailItem? row)
|
||||
{
|
||||
if (row == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
WarehouseAreaPickerDialogViewModel? pickerVm = null;
|
||||
bool confirmed;
|
||||
try
|
||||
{
|
||||
confirmed = await HandyControl.Controls.Dialog.Show<WarehouseAreaPickerDialogView>()
|
||||
.Initialize<WarehouseAreaPickerDialogViewModel>(vm =>
|
||||
{
|
||||
pickerVm = vm;
|
||||
vm.Initialize(row.WarehouseLocation);
|
||||
})
|
||||
.GetResultAsync<bool>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 保留 Debug 输出以便排查,但不阻断流程
|
||||
System.Diagnostics.Debug.WriteLine($"[库位选择] 弹窗异常: {ex}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!confirmed || pickerVm?.SelectedRecord == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 仅回填库区名称(与后端 warehouse_location 字段保持单字符串语义)。
|
||||
// 如未来需要严格关联库区 ID,可在 RawMaterialSplitDetailItem 新增 WarehouseAreaId 一并保存。
|
||||
row.WarehouseLocation = pickerVm.SelectedRecord.AreaName;
|
||||
}
|
||||
|
||||
private async Task OpenMaterialPickerAsync()
|
||||
@@ -537,7 +616,7 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
|
||||
/// <summary>
|
||||
/// 新增模式下为已在前端隐藏的字段补充默认值,确保保存到后端时不为空:
|
||||
/// 检测结果=未检、检测状态=送样、打印标记=未打印、入库结存=否。
|
||||
/// 检测结果=未检、打印标记=未打印、入库结存=否。
|
||||
/// 字典就绪时取字典 code,未就绪时回退到约定的常用 code。
|
||||
/// </summary>
|
||||
protected void ApplyHiddenFieldDefaultsForAdd()
|
||||
@@ -551,10 +630,7 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
{
|
||||
Entry.TestResult = ResolveDefaultOptionValue(TestResultOptions, "未检", "0");
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(Entry.TestStatus))
|
||||
{
|
||||
Entry.TestStatus = ResolveDefaultOptionValue(TestStatusOptions, "送样", "0");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(Entry.PrintFlag))
|
||||
{
|
||||
Entry.PrintFlag = ResolveDefaultOptionValue(PrintFlagOptions, "未打印", "0");
|
||||
@@ -582,22 +658,58 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
{
|
||||
SplitCodeDetails.Clear();
|
||||
|
||||
// 三个字段是按拆码明细多行拼接的字符串(末尾带 /),解析回明细列表
|
||||
// 六个字段都是按拆码明细多行拼接的字符串(末尾带 /),解析回明细列表
|
||||
var portionsArr = SplitJoinedValues(Entry?.TotalPortions);
|
||||
var weightArr = SplitJoinedValues(Entry?.PortionWeight);
|
||||
var packagesArr = SplitJoinedValues(Entry?.PortionPackages);
|
||||
var rowCount = Math.Max(1, Math.Max(portionsArr.Length, Math.Max(weightArr.Length, packagesArr.Length)));
|
||||
var locationsArr = SplitJoinedValues(Entry?.PortionWarehouseLocations);
|
||||
var idsArr = SplitJoinedValues(Entry?.PortionDetailIds);
|
||||
var cardFlagsArr = SplitJoinedValues(Entry?.PortionCardFlags);
|
||||
// 历史记录可能没存 PortionCardFlags:用 print_flag 推断(与原行为兼容)
|
||||
var fallbackToPrintFlag = cardFlagsArr.Length == 0
|
||||
&& string.Equals(Entry?.PrintFlag, "1", StringComparison.Ordinal);
|
||||
var rowCount = Math.Max(1, Math.Max(Math.Max(portionsArr.Length, weightArr.Length),
|
||||
Math.Max(Math.Max(packagesArr.Length, locationsArr.Length), idsArr.Length)));
|
||||
|
||||
for (var i = 0; i < rowCount; i++)
|
||||
{
|
||||
SplitCodeDetails.Add(new RawMaterialSplitDetailItem
|
||||
var locationFromArr = GetAt(locationsArr, i);
|
||||
// 兼容历史数据:明细库位拼接字段为空时,首行回退到 Entry.WarehouseLocation
|
||||
// (早期版本里整票级库位曾被反写过,避免老记录打开后明细全空)
|
||||
var locationFallback = (i == 0 && string.IsNullOrWhiteSpace(locationFromArr))
|
||||
? Entry?.WarehouseLocation
|
||||
: locationFromArr;
|
||||
|
||||
var item = new RawMaterialSplitDetailItem
|
||||
{
|
||||
Portions = TryParseInt(GetAt(portionsArr, i)),
|
||||
PortionWeight = TryParseDouble(GetAt(weightArr, i)),
|
||||
PortionPackages = TryParseInt(GetAt(packagesArr, i)),
|
||||
// 库位是单值字段,仅赋给首行
|
||||
WarehouseLocation = i == 0 ? Entry?.WarehouseLocation : null,
|
||||
});
|
||||
WarehouseLocation = locationFallback,
|
||||
};
|
||||
// 仅当后端已持久化了该行 ID 时才覆盖默认值,确保跨次保持稳定;
|
||||
// 历史记录无 ID 字段时使用构造期生成的新 GUID(之后保存会回填)。
|
||||
var existingId = GetAt(idsArr, i);
|
||||
if (!string.IsNullOrWhiteSpace(existingId))
|
||||
{
|
||||
item.Id = existingId;
|
||||
}
|
||||
// HasCard 行级解析(优先级 1):从 PortionCardFlags 按位读取,"1"=已生成。
|
||||
// 这是「保存后新增未生成行不被误判为已打印」的关键 — 新增行保存时 HasCard=false 持久化为 "0",
|
||||
// 重新加载时回填 false,「生成原材料卡片」就能正确把它列入待生成清单。
|
||||
var flagAt = GetAt(cardFlagsArr, i);
|
||||
if (!string.IsNullOrWhiteSpace(flagAt))
|
||||
{
|
||||
item.HasCard = string.Equals(flagAt, "1", StringComparison.Ordinal);
|
||||
}
|
||||
else if (fallbackToPrintFlag && !string.IsNullOrWhiteSpace(existingId))
|
||||
{
|
||||
// 优先级 2(兼容历史记录):未存 PortionCardFlags 时,沿用旧逻辑——
|
||||
// 持久化 ID 存在 + 整票已打印 ⇒ 视为已生成卡片。
|
||||
item.HasCard = true;
|
||||
}
|
||||
// 否则保持构造默认值 false(新增态、未打印整票等场景)
|
||||
SplitCodeDetails.Add(item);
|
||||
}
|
||||
|
||||
RaisePropertyChanged(nameof(SplitCodeTableHeight));
|
||||
@@ -666,6 +778,14 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
|
||||
private void OnSplitDetailItemPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
||||
{
|
||||
// 用户填写或修改某行的「份数」时,按公式自动算出该行「每份重量」:
|
||||
// 每份重量 = (总重 - Σ其他行的份数×每份重量) / 当前行份数
|
||||
if (e.PropertyName == nameof(RawMaterialSplitDetailItem.Portions)
|
||||
&& sender is RawMaterialSplitDetailItem row)
|
||||
{
|
||||
RecalculatePortionWeightForRow(row);
|
||||
}
|
||||
|
||||
if (e.PropertyName is nameof(RawMaterialSplitDetailItem.Portions)
|
||||
or nameof(RawMaterialSplitDetailItem.PortionWeight)
|
||||
or nameof(RawMaterialSplitDetailItem.PortionPackages))
|
||||
@@ -674,6 +794,42 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 拆码明细某行的「份数」变化时,按公式重算该行「每份重量」:
|
||||
/// 公式:每份重量 = (总重 - 其他行 Σ份数×每份重量) / 当前行份数
|
||||
/// — 用户单独手改「每份重量」不触发;
|
||||
/// — 总重为空 / ≤0、当前份数 ≤0、剩余总重 ≤0 时跳过;
|
||||
/// — 结果四舍五入到两位小数;
|
||||
/// — 写入 row.PortionWeight 会触发 PropertyChanged(PortionWeight),不会再次进入本方法(PropertyName 已不是 Portions),不会形成循环。
|
||||
/// </summary>
|
||||
private void RecalculatePortionWeightForRow(RawMaterialSplitDetailItem row)
|
||||
{
|
||||
if (Entry?.TotalWeight is not { } total || total <= 0d)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (row.Portions is not { } portions || portions <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var otherSum = 0d;
|
||||
foreach (var other in SplitCodeDetails)
|
||||
{
|
||||
if (ReferenceEquals(other, row)) continue;
|
||||
if (other.Portions is not { } op || other.PortionWeight is not { } ow) continue;
|
||||
otherSum += op * ow;
|
||||
}
|
||||
|
||||
var remaining = total - otherSum;
|
||||
if (remaining <= 0d)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
row.PortionWeight = Math.Round(remaining / portions, 2);
|
||||
}
|
||||
|
||||
private void RaiseSplitDisplayPropertyChanged()
|
||||
{
|
||||
RaisePropertyChanged(nameof(SplitTotalPortionsDisplay));
|
||||
@@ -708,21 +864,26 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 把 SplitCodeDetails 全部明细行的「份数 / 每份重量 / 每份包数」按 "x/y/z/" 拼接后持久化到 Entry,
|
||||
/// 库位仍取首行(业务上库位是单值)。与 SplitTotalPortionsDisplay 等只读展示属性同规则。
|
||||
/// 把 SplitCodeDetails 全部明细行的「份数 / 每份重量 / 每份包数 / 库位 / 行 ID」按 "x/y/z/" 拼接后持久化到 Entry。
|
||||
/// 明细行的「库位」属于行级数据(用于后续生成原材料卡片时区分库位),与 Entry.WarehouseLocation
|
||||
/// (基础资料整票级单值)独立保存,通过 portion_warehouse_locations 字段持久化。
|
||||
/// 子类(如 OperationViewModel 的「重新拆码」)需要直接调用,因此声明为 protected。
|
||||
/// </summary>
|
||||
private void ApplySplitDetailsToEntry()
|
||||
protected void ApplySplitDetailsToEntry()
|
||||
{
|
||||
if (Entry == null) return;
|
||||
|
||||
Entry.TotalPortions = JoinSplitValue(it => it.Portions?.ToString(CultureInfo.InvariantCulture), true);
|
||||
Entry.PortionWeight = JoinSplitValue(it => FormatNullableDecimal(it.PortionWeight), true);
|
||||
Entry.PortionPackages = JoinSplitValue(it => it.PortionPackages?.ToString(CultureInfo.InvariantCulture), true);
|
||||
|
||||
if (SplitCodeDetails.Count > 0)
|
||||
{
|
||||
Entry.WarehouseLocation = SplitCodeDetails[0].WarehouseLocation;
|
||||
}
|
||||
Entry.PortionWarehouseLocations = JoinSplitValue(it => string.IsNullOrWhiteSpace(it.WarehouseLocation) ? null : it.WarehouseLocation.Trim(), true);
|
||||
// 持久化每行 GUID,行序与上方四个字段对齐;JoinSplitValue 会过滤空,但 Id 在构造时即生成不会为空。
|
||||
Entry.PortionDetailIds = JoinSplitValue(it => it.Id, true);
|
||||
// 持久化每行的「已生成卡片」标志(1=已生成 0=未生成),
|
||||
// 用于解决「保存后新增行被误判为已打印」的问题:
|
||||
// 「生成原材料卡片」只对 HasCard=false 的行加卡 → 必须把真实的 HasCard 持久化,
|
||||
// 否则重新加载后无法区分「已生成」与「保存后新增的未生成行」。
|
||||
Entry.PortionCardFlags = JoinSplitValue(it => it.HasCard ? "1" : "0", true);
|
||||
}
|
||||
|
||||
private double CalculateSplitCodeTableHeight()
|
||||
@@ -740,6 +901,14 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
|
||||
|
||||
public class RawMaterialSplitDetailItem : BindableBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 拆码明细行 ID(GUID,构造时生成)。前端隐藏;
|
||||
/// 「生成原材料卡片」时由桌面端写入对应 MesXslRawMaterialCard.SplitDetailId;
|
||||
/// 「重新拆码」时按入场记录的 PortionDetailIds 反查批删关联卡片。
|
||||
/// 编辑回填时若 portion_detail_ids 已有该位置的值,会覆盖默认值,确保跨次保持稳定。
|
||||
/// </summary>
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString("N");
|
||||
|
||||
private int? _portions;
|
||||
public int? Portions
|
||||
{
|
||||
@@ -767,4 +936,18 @@ public class RawMaterialSplitDetailItem : BindableBase
|
||||
get => _warehouseLocation;
|
||||
set => SetProperty(ref _warehouseLocation, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 该行是否已生成原材料卡片(运行时状态,不直接持久化)。
|
||||
/// 加载时根据 Entry.PrintFlag + Id 是否来自持久化的 PortionDetailIds 推断;
|
||||
/// 「生成原材料卡片」成功后由 VM 置为 true;「重新拆码」清空集合时随之失效。
|
||||
/// true → 行内输入项 / 删除 全部锁定,避免与已生成卡片数据脱节;
|
||||
/// false → 该行参与下次「生成原材料卡片」(继续拆码 + 续号生成)。
|
||||
/// </summary>
|
||||
private bool _hasCard;
|
||||
public bool HasCard
|
||||
{
|
||||
get => _hasCard;
|
||||
set => SetProperty(ref _hasCard, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,20 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
|
||||
|
||||
public ObservableCollection<MesXslRawMaterialEntry> TodayEntries { get; } = new();
|
||||
|
||||
public IReadOnlyList<string> DateRangeOptions { get; } =
|
||||
["今日", "过去24小时", "过去48小时", "过去72小时"];
|
||||
|
||||
private string _selectedDateRange = "今日";
|
||||
public string SelectedDateRange
|
||||
{
|
||||
get => _selectedDateRange;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _selectedDateRange, value))
|
||||
_ = LoadTodayEntriesAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private MesXslRawMaterialEntry? _selectedTodayEntry;
|
||||
public MesXslRawMaterialEntry? SelectedTodayEntry
|
||||
{
|
||||
@@ -67,12 +81,20 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>仅当入场记录已保存(有 Id)且存在拆码明细时允许生成原材料卡片。</summary>
|
||||
public bool CanGenerateCards => !string.IsNullOrWhiteSpace(Entry?.Id) && SplitCodeDetails.Count > 0;
|
||||
/// <summary>
|
||||
/// 「生成原材料卡片」按钮可用条件:
|
||||
/// 入场记录已保存(有 Id)且至少存在一行「未生成卡片 + 份数>0」的明细。
|
||||
/// 已打印态下用户「继续拆码」新增了行,按钮自动重新可用;全部行都已生成卡片时不可用。
|
||||
/// </summary>
|
||||
public bool CanGenerateCards =>
|
||||
!string.IsNullOrWhiteSpace(Entry?.Id)
|
||||
&& SplitCodeDetails.Any(d => !d.HasCard && (d.Portions ?? 0) > 0);
|
||||
|
||||
public DelegateCommand ToggleRightPanelCommand { get; }
|
||||
public DelegateCommand RefreshTodayEntriesCommand { get; }
|
||||
public DelegateCommand GenerateRawMaterialCardsCommand { get; }
|
||||
/// <summary>「重新拆码」:清除已生成的卡片 + 清空明细,仅在编辑态可用。</summary>
|
||||
public DelegateCommand ResplitCommand { get; }
|
||||
|
||||
public RawMaterialEntryOperationViewModel(
|
||||
IRawMaterialEntryService entryService,
|
||||
@@ -88,7 +110,52 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
|
||||
ToggleRightPanelCommand = new DelegateCommand(() => IsRightPanelExpanded = !IsRightPanelExpanded);
|
||||
RefreshTodayEntriesCommand = new DelegateCommand(async () => await LoadTodayEntriesAsync());
|
||||
GenerateRawMaterialCardsCommand = new DelegateCommand(async () => await GenerateRawMaterialCardsAsync());
|
||||
SplitCodeDetails.CollectionChanged += (_, _) => RaisePropertyChanged(nameof(CanGenerateCards));
|
||||
ResplitCommand = new DelegateCommand(async () => await ResplitAsync(), () => CanResplit)
|
||||
.ObservesProperty(() => Entry);
|
||||
// 集合变化:批量重订阅 item.PropertyChanged 监听 HasCard/Portions,并同步刷新两个 Can*。
|
||||
SplitCodeDetails.CollectionChanged += OnSplitCodeDetailsCollectionChangedForCanFlags;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 「重新拆码」按钮可用条件:入场记录已保存 + 至少有一行已生成卡片。
|
||||
/// 全是新增未生成的行时,用户直接点「删除」即可,无需走「清空卡片」流程。
|
||||
/// </summary>
|
||||
public bool CanResplit =>
|
||||
!string.IsNullOrWhiteSpace(Entry?.Id)
|
||||
&& SplitCodeDetails.Any(d => d.HasCard);
|
||||
|
||||
/// <summary>
|
||||
/// 集合变化时:对新增/移除的 item 维护 PropertyChanged 订阅,并刷新两个 Can* 属性。
|
||||
/// HasCard / Portions 变化会让「生成原材料卡片」和「重新拆码」按钮可用性同步刷新。
|
||||
/// </summary>
|
||||
private void OnSplitCodeDetailsCollectionChangedForCanFlags(
|
||||
object? sender,
|
||||
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
if (e.OldItems != null)
|
||||
{
|
||||
foreach (RawMaterialSplitDetailItem o in e.OldItems)
|
||||
o.PropertyChanged -= OnSplitDetailHasCardChanged;
|
||||
}
|
||||
if (e.NewItems != null)
|
||||
{
|
||||
foreach (RawMaterialSplitDetailItem n in e.NewItems)
|
||||
n.PropertyChanged += OnSplitDetailHasCardChanged;
|
||||
}
|
||||
RaisePropertyChanged(nameof(CanGenerateCards));
|
||||
RaisePropertyChanged(nameof(CanResplit));
|
||||
ResplitCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
|
||||
private void OnSplitDetailHasCardChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
|
||||
{
|
||||
if (e.PropertyName is nameof(RawMaterialSplitDetailItem.HasCard)
|
||||
or nameof(RawMaterialSplitDetailItem.Portions))
|
||||
{
|
||||
RaisePropertyChanged(nameof(CanGenerateCards));
|
||||
RaisePropertyChanged(nameof(CanResplit));
|
||||
ResplitCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
public override void InitializeForAdd()
|
||||
@@ -117,9 +184,15 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
|
||||
{
|
||||
IsLoading = true;
|
||||
var result = await EntryService.PageAsync(1, TodayListFetchSize);
|
||||
var today = DateTime.Today;
|
||||
DateTime? threshold = _selectedDateRange switch
|
||||
{
|
||||
"过去24小时" => DateTime.Now.AddHours(-24),
|
||||
"过去48小时" => DateTime.Now.AddHours(-48),
|
||||
"过去72小时" => DateTime.Now.AddHours(-72),
|
||||
_ => null // "今日":按自然日过滤
|
||||
};
|
||||
var rows = result.Records
|
||||
.Where(e => IsTodayEntry(e, today))
|
||||
.Where(e => IsInRange(e, threshold))
|
||||
.OrderByDescending(e => e.EntryTime ?? e.CreateTime ?? DateTime.MinValue)
|
||||
.ToList();
|
||||
TodayEntries.Clear();
|
||||
@@ -136,11 +209,14 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTodayEntry(MesXslRawMaterialEntry e, DateTime today)
|
||||
private static bool IsInRange(MesXslRawMaterialEntry e, DateTime? threshold)
|
||||
{
|
||||
var byEntry = e.EntryTime?.Date == today;
|
||||
var byCreate = e.CreateTime?.Date == today;
|
||||
return byEntry || byCreate;
|
||||
if (threshold == null)
|
||||
{
|
||||
var today = DateTime.Today;
|
||||
return (e.EntryTime?.Date == today) || (e.CreateTime?.Date == today);
|
||||
}
|
||||
return (e.EntryTime >= threshold) || (e.CreateTime >= threshold);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -158,20 +234,118 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
|
||||
await LoadMaterialOptionsAsync();
|
||||
}
|
||||
|
||||
/// <summary>编辑保存成功后:刷新右侧今日列表并切回新增态,避免连续误改同一条。</summary>
|
||||
/// <summary>
|
||||
/// 保存成功后:无论新增还是编辑,都立即刷新右侧「今日入场」列表。
|
||||
/// 编辑成功额外把表单切回「新增态」,避免用户连续误改同一条;新增成功后由基类负责清空表单。
|
||||
/// </summary>
|
||||
protected override async Task SaveAsync()
|
||||
{
|
||||
var wasEdit = !IsAddMode;
|
||||
await base.SaveAsync();
|
||||
RaisePropertyChanged(nameof(CanGenerateCards));
|
||||
if (wasEdit && Result && CloseAction == null)
|
||||
|
||||
if (!Result || CloseAction != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await LoadTodayEntriesAsync();
|
||||
|
||||
if (wasEdit)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Success("编辑成功!");
|
||||
await LoadTodayEntriesAsync();
|
||||
InitializeForAdd();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 「重新拆码」:弹窗预提示「将清除 N 张原材料卡片」→ 确认 → 后端按 splitDetailId IN 批删
|
||||
/// → 清空 SplitCodeDetails → Entry.PrintFlag=0 → EditAsync(Entry) 持久化新状态 → 刷新今日列表。
|
||||
/// 流程对应「清空卡片重新生成」的业务诉求;用户可立即重新维护明细并再次「生成原材料卡片」。
|
||||
/// </summary>
|
||||
private async Task ResplitAsync()
|
||||
{
|
||||
if (Entry == null || string.IsNullOrWhiteSpace(Entry.Id))
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Warning("请先选中一条已保存的入场记录后再「重新拆码」。");
|
||||
return;
|
||||
}
|
||||
var detailIds = SplitCodeDetails
|
||||
.Select(d => d.Id)
|
||||
.Where(id => !string.IsNullOrWhiteSpace(id))
|
||||
.Distinct()
|
||||
.ToList();
|
||||
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
|
||||
// dryRun 预查:统计当前明细行已生成的卡片数量
|
||||
var cardCount = detailIds.Count > 0
|
||||
? await _rawMaterialCardService.DeleteBySplitDetailIdsAsync(detailIds, dryRun: true)
|
||||
: 0;
|
||||
if (cardCount < 0)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Error("无法连接服务器,重新拆码已取消。");
|
||||
return;
|
||||
}
|
||||
|
||||
var confirm = HandyControl.Controls.MessageBox.Show(
|
||||
$"当前入场记录已生成 {cardCount} 条原材料卡片,是否清空卡片重新生成?",
|
||||
"重新拆码",
|
||||
System.Windows.MessageBoxButton.OKCancel,
|
||||
System.Windows.MessageBoxImage.Question);
|
||||
if (confirm != System.Windows.MessageBoxResult.OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 真正执行批删(即使 cardCount=0 也调用一次,结果幂等)
|
||||
if (cardCount > 0)
|
||||
{
|
||||
var deleted = await _rawMaterialCardService.DeleteBySplitDetailIdsAsync(detailIds, dryRun: false);
|
||||
if (deleted < 0)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Error("清除已生成的原材料卡片失败,重新拆码已中止。");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 清空明细 + 重置打印标记,并把变更持久化到后端入场记录
|
||||
SplitCodeDetails.Clear();
|
||||
Entry.PrintFlag = "0";
|
||||
ApplySplitDetailsToEntry(); // 同步把 PortionDetailIds 等清空到 Entry 上
|
||||
var ok = await EntryService.EditAsync(Entry);
|
||||
if (!ok)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Error("保存重新拆码后的入场记录失败,请刷新后重试。");
|
||||
return;
|
||||
}
|
||||
|
||||
RaisePropertyChanged(nameof(Entry));
|
||||
RaisePropertyChanged(nameof(IsPrinted));
|
||||
RaisePropertyChanged(nameof(IsTotalWeightEditable));
|
||||
AddSplitDetailCommand.RaiseCanExecuteChanged();
|
||||
RaisePropertyChanged(nameof(CanGenerateCards));
|
||||
RaisePropertyChanged(nameof(CanResplit));
|
||||
HandyControl.Controls.MessageBox.Success($"已清除 {cardCount} 条原材料卡片,请重新维护拆码明细。");
|
||||
await LoadTodayEntriesAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Error($"重新拆码失败:{ex.Message}");
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 「生成原材料卡片」:仅处理 HasCard==false 的明细行(「继续拆码」流程的核心)。
|
||||
/// 条码续号 = 已有卡片总数(Σ HasCard==true 行的 Portions)+ 1,避免与已生成卡片的条码冲突。
|
||||
/// 成功生成的行置 HasCard=true,Entry.PrintFlag=1(整票级标记)。
|
||||
/// </summary>
|
||||
private async Task GenerateRawMaterialCardsAsync()
|
||||
{
|
||||
if (Entry == null || string.IsNullOrWhiteSpace(Entry.Id))
|
||||
@@ -179,26 +353,73 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
|
||||
HandyControl.Controls.MessageBox.Warning("请先保存入场记录再生成原材料卡片!");
|
||||
return;
|
||||
}
|
||||
if (SplitCodeDetails.Count == 0)
|
||||
|
||||
// 只处理未生成卡片的行 + 份数>0;已生成行直接跳过,避免重复加卡和条码冲突
|
||||
var pendingRows = SplitCodeDetails
|
||||
.Where(d => !d.HasCard && (d.Portions ?? 0) > 0)
|
||||
.ToList();
|
||||
if (pendingRows.Count == 0)
|
||||
{
|
||||
HandyControl.Controls.MessageBox.Warning("当前入场记录无拆分明细,无法生成原材料卡片!");
|
||||
HandyControl.Controls.MessageBox.Warning("当前没有待生成卡片的拆码明细(已生成的行不会重复处理)。请先「新增明细」再生成。");
|
||||
return;
|
||||
}
|
||||
|
||||
// 库位必填仅校验本批待生成行;行号取在原集合的真实索引,避免编号偏移误导
|
||||
foreach (var row in pendingRows)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(row.WarehouseLocation))
|
||||
{
|
||||
var realIndex = SplitCodeDetails.IndexOf(row);
|
||||
HandyControl.Controls.MessageBox.Warning($"拆码明细第 {realIndex + 1} 行的「库位」未填写,请点击该行库位选择库区后再生成原材料卡片。");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 总重核对:拆码明细合计 vs 基础资料总重
|
||||
{
|
||||
var splitTotal = SplitCodeDetails.Sum(d => (d.PortionWeight ?? 0d) * (d.Portions ?? 0));
|
||||
var basicTotal = Entry.TotalWeight ?? 0d;
|
||||
if (Math.Abs(splitTotal - basicTotal) > 0.01d)
|
||||
{
|
||||
string hint;
|
||||
if (splitTotal > basicTotal)
|
||||
hint = $"本次打印拆码总重 {splitTotal:0.##},超出基础资料总重 {basicTotal:0.##},超出部分是否需要核对?";
|
||||
else
|
||||
hint = $"本次打印拆码总重 {splitTotal:0.##},少于基础资料总重 {basicTotal:0.##},剩余部分将无法继续拆码,是否继续?";
|
||||
var confirm = System.Windows.MessageBox.Show(
|
||||
hint,
|
||||
"总重核对",
|
||||
System.Windows.MessageBoxButton.OKCancel,
|
||||
System.Windows.MessageBoxImage.Warning);
|
||||
if (confirm != System.Windows.MessageBoxResult.OK) return;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
var globalIndex = 1;
|
||||
// 续号起点:已有卡片数 = Σ HasCard==true 行的 Portions;新增卡片从其后续编
|
||||
var alreadyGenerated = SplitCodeDetails
|
||||
.Where(d => d.HasCard)
|
||||
.Sum(d => d.Portions ?? 0);
|
||||
var globalIndex = alreadyGenerated + 1;
|
||||
var baseBarcode = Entry.Barcode ?? "";
|
||||
var failCount = 0;
|
||||
var newCardCount = 0;
|
||||
// 按行收集成功标记:只要该行所有份都加卡成功,行 HasCard=true;
|
||||
// 中途任一份失败则保留 HasCard=false,下次再点「生成」时会重试该行
|
||||
var rowSuccessMap = new Dictionary<RawMaterialSplitDetailItem, bool>();
|
||||
|
||||
foreach (var detail in SplitCodeDetails)
|
||||
foreach (var detail in pendingRows)
|
||||
{
|
||||
var portions = detail.Portions ?? 0;
|
||||
var rowAllOk = true;
|
||||
for (var i = 0; i < portions; i++)
|
||||
{
|
||||
var card = new MesXslRawMaterialCard
|
||||
{
|
||||
// 关联到当前拆码明细行 — 便于「重新拆码」按此 ID 批删
|
||||
SplitDetailId = detail.Id,
|
||||
Barcode = baseBarcode + globalIndex.ToString("D3"),
|
||||
BatchNo = Entry.BatchNo,
|
||||
EntryDate = Entry.EntryTime?.Date ?? DateTime.Today,
|
||||
@@ -219,21 +440,39 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
|
||||
TenantId = Entry.TenantId
|
||||
};
|
||||
var ok = await _rawMaterialCardService.AddAsync(card);
|
||||
if (!ok) failCount++;
|
||||
if (ok) newCardCount++;
|
||||
else { failCount++; rowAllOk = false; }
|
||||
globalIndex++;
|
||||
}
|
||||
rowSuccessMap[detail] = rowAllOk;
|
||||
}
|
||||
|
||||
// 更新打印状态为「已打印」
|
||||
Entry.PrintFlag = "1";
|
||||
await EntryService.EditAsync(Entry);
|
||||
RaisePropertyChanged(nameof(Entry));
|
||||
// 把整行加卡都成功的标记 HasCard=true(触发该行 UI 自动锁定 + 删除按钮隐藏 + 打印列显示「已打印」)
|
||||
foreach (var kv in rowSuccessMap)
|
||||
{
|
||||
if (kv.Value) kv.Key.HasCard = true;
|
||||
}
|
||||
|
||||
// 整票级 PrintFlag:只要本批至少有一张卡片成功即置 1(与原行为兼容)。
|
||||
// 关键:必须先 ApplySplitDetailsToEntry(),把刚被设为 true 的 HasCard 序列化到 PortionCardFlags,
|
||||
// 否则下次重新打开时该行会回退为 false(PortionCardFlags 旧值未更新),从而被「继续拆码」流程误纳入待生成。
|
||||
if (newCardCount > 0)
|
||||
{
|
||||
Entry.PrintFlag = "1";
|
||||
ApplySplitDetailsToEntry();
|
||||
await EntryService.EditAsync(Entry);
|
||||
RaisePropertyChanged(nameof(Entry));
|
||||
RaisePropertyChanged(nameof(IsPrinted));
|
||||
RaisePropertyChanged(nameof(IsTotalWeightEditable));
|
||||
AddSplitDetailCommand.RaiseCanExecuteChanged();
|
||||
}
|
||||
RaisePropertyChanged(nameof(CanGenerateCards));
|
||||
RaisePropertyChanged(nameof(CanResplit));
|
||||
|
||||
var total = globalIndex - 1;
|
||||
if (failCount == 0)
|
||||
HandyControl.Controls.MessageBox.Success($"已生成 {total} 张原材料卡片,打印状态已更新为「已打印」!");
|
||||
HandyControl.Controls.MessageBox.Success($"已生成 {newCardCount} 张原材料卡片(累计 {alreadyGenerated + newCardCount} 张),打印状态已更新为「已打印」!");
|
||||
else
|
||||
HandyControl.Controls.MessageBox.Warning($"共生成 {total} 张,其中 {failCount} 张失败,请检查网络后重试!");
|
||||
HandyControl.Controls.MessageBox.Warning($"本次共尝试生成 {newCardCount + failCount} 张,成功 {newCardCount} 张,失败 {failCount} 张。失败的行未标记为「已打印」,可检查网络后再次点击「生成原材料卡片」重试。");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
using HandyControl.Tools.Extension;
|
||||
using System.Collections.ObjectModel;
|
||||
using YY.Admin.Core;
|
||||
using YY.Admin.Core.Entity;
|
||||
using YY.Admin.Core.Services;
|
||||
|
||||
namespace YY.Admin.ViewModels.RawMaterialEntry;
|
||||
|
||||
/// <summary>
|
||||
/// 库区选择弹窗 ViewModel。
|
||||
/// 用于「拆码明细 → 库位」字段:点击库位输入框时弹出本对话框,支持按名称/编码搜索,单选确认。
|
||||
/// </summary>
|
||||
public class WarehouseAreaPickerDialogViewModel : BaseViewModel, IDialogResultable<bool>
|
||||
{
|
||||
private readonly IWarehouseAreaService _warehouseAreaService;
|
||||
|
||||
private string? _searchAreaName;
|
||||
public string? SearchAreaName
|
||||
{
|
||||
get => _searchAreaName;
|
||||
set => SetProperty(ref _searchAreaName, value);
|
||||
}
|
||||
|
||||
private string? _searchAreaCode;
|
||||
public string? SearchAreaCode
|
||||
{
|
||||
get => _searchAreaCode;
|
||||
set => SetProperty(ref _searchAreaCode, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<MesXslWarehouseArea> Records { get; } = new();
|
||||
|
||||
private MesXslWarehouseArea? _selectedRecord;
|
||||
public MesXslWarehouseArea? SelectedRecord
|
||||
{
|
||||
get => _selectedRecord;
|
||||
set
|
||||
{
|
||||
SetProperty(ref _selectedRecord, value);
|
||||
ConfirmCommand.RaiseCanExecuteChanged();
|
||||
RaisePropertyChanged(nameof(SelectedRecordDisplay));
|
||||
RaisePropertyChanged(nameof(HasSelectedRecord));
|
||||
}
|
||||
}
|
||||
|
||||
public string SelectedRecordDisplay => _selectedRecord != null
|
||||
? $"[{_selectedRecord.AreaCode}] {_selectedRecord.AreaName} · 仓库:{_selectedRecord.WarehouseName ?? "-"}"
|
||||
: "选中库区后点击「确认选择」";
|
||||
|
||||
public bool HasSelectedRecord => _selectedRecord != null;
|
||||
|
||||
private bool _result;
|
||||
public bool Result
|
||||
{
|
||||
get => _result;
|
||||
set => SetProperty(ref _result, value);
|
||||
}
|
||||
|
||||
public Action? CloseAction { get; set; }
|
||||
|
||||
public DelegateCommand SearchCommand { get; }
|
||||
public DelegateCommand ConfirmCommand { get; }
|
||||
public DelegateCommand CancelCommand { get; }
|
||||
|
||||
public WarehouseAreaPickerDialogViewModel(
|
||||
IWarehouseAreaService warehouseAreaService,
|
||||
IContainerExtension container,
|
||||
IRegionManager regionManager) : base(container, regionManager)
|
||||
{
|
||||
_warehouseAreaService = warehouseAreaService;
|
||||
SearchCommand = new DelegateCommand(async () => await LoadAsync());
|
||||
ConfirmCommand = new DelegateCommand(Confirm, () => SelectedRecord != null);
|
||||
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
|
||||
_ = LoadAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 可由外部传入「当前已选库区名称」用于打开后高亮预选。
|
||||
/// </summary>
|
||||
public void Initialize(string? currentAreaName)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(currentAreaName))
|
||||
{
|
||||
SearchAreaName = currentAreaName.Trim();
|
||||
_ = LoadAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task LoadAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
IsLoading = true;
|
||||
var result = await _warehouseAreaService.PageAsync(
|
||||
1,
|
||||
500,
|
||||
areaCode: SearchAreaCode?.Trim(),
|
||||
areaName: SearchAreaName?.Trim(),
|
||||
warehouseId: null,
|
||||
status: "0");
|
||||
|
||||
Records.Clear();
|
||||
foreach (var record in result.Records)
|
||||
{
|
||||
Records.Add(record);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(SearchAreaName))
|
||||
{
|
||||
SelectedRecord = Records.FirstOrDefault(x =>
|
||||
string.Equals(x.AreaName, SearchAreaName, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
Records.Clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
IsLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void Confirm()
|
||||
{
|
||||
if (SelectedRecord == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Result = true;
|
||||
CloseAction?.Invoke();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user