新增密炼物料皮重策略功能,包括相关实体、服务、控制器及接口,支持桌面端免密CRUD操作,优化打印记录与原料入场记录的衍生字段填充逻辑,提升用户体验。

This commit is contained in:
geht
2026-06-02 16:28:51 +08:00
parent 37239e1b0a
commit fef7d25e3c
75 changed files with 4407 additions and 170 deletions

View File

@@ -162,6 +162,10 @@ public class StompWebSocketService : ISignalRService
await SendFrameAsync(
BuildSubscribeFrame("sub-mes-warehouse-areas", "/topic/sync/mes-warehouse-areas"),
cancellationToken).ConfigureAwait(false);
// 密炼物料皮重策略变更:订阅 /topic/sync/mes-mixer-material-tare-strategies
await SendFrameAsync(
BuildSubscribeFrame("sub-mes-mixer-material-tare-strategies", "/topic/sync/mes-mixer-material-tare-strategies"),
cancellationToken).ConfigureAwait(false);
// 打印模板变更:订阅 /topic/sync/print-templates
await SendFrameAsync(
BuildSubscribeFrame("sub-print-templates", "/topic/sync/print-templates"),

View File

@@ -17,6 +17,7 @@ using YY.Admin.Views.RawMaterialCard;
using YY.Admin.Views.WarehouseArea;
using YY.Admin.Views.RawMaterialEntry;
using YY.Admin.Views.Print;
using YY.Admin.Views.MixerMaterialTareStrategy;
namespace YY.Admin
{
@@ -92,6 +93,8 @@ namespace YY.Admin
containerRegistry.RegisterForNavigation<RawMaterialCardListView>();
// 库区管理
containerRegistry.RegisterForNavigation<WarehouseAreaListView>();
// 密炼物料皮重策略
containerRegistry.RegisterForNavigation<MixerMaterialTareStrategyListView>();
// 打印设置
containerRegistry.RegisterForNavigation<PrintSettingsView>();
// 打印模板列表

View File

@@ -17,6 +17,7 @@ using YY.Admin.Services.Service;
using YY.Admin.Services.Service.Category;
using YY.Admin.Services.Service.Customer;
using YY.Admin.Services.Service.Dict;
using YY.Admin.Services.Service.MixerMaterialTareStrategy;
using YY.Admin.Services.Service.MixerMaterial;
using YY.Admin.Services.Service.Supplier;
using YY.Admin.Services.Service.RawMaterialCard;
@@ -69,6 +70,9 @@ public class SyncModule : IModule
// 库区管理:免密 API 直连 + STOMP 实时通知
containerRegistry.RegisterSingleton<IWarehouseAreaService, WarehouseAreaService>();
containerRegistry.RegisterSingleton<WarehouseAreaSyncCoordinator>();
// 密炼物料皮重策略:免密 API 直连 + STOMP 实时通知
containerRegistry.RegisterSingleton<IMixerMaterialTareStrategyService, MixerMaterialTareStrategyService>();
containerRegistry.RegisterSingleton<MixerMaterialTareStrategySyncCoordinator>();
// 分类字典:启动同步 + 断线重连补刷
containerRegistry.RegisterSingleton<CategorySyncCoordinator>();
// 数据字典:启动同步 + 断线重连补刷
@@ -143,6 +147,8 @@ public class SyncModule : IModule
_ = containerProvider.Resolve<RawMaterialCardSyncCoordinator>();
// 强制实例化库区同步协调器
_ = containerProvider.Resolve<WarehouseAreaSyncCoordinator>();
// 强制实例化密炼物料皮重策略同步协调器
_ = containerProvider.Resolve<MixerMaterialTareStrategySyncCoordinator>();
// 强制实例化分类字典同步协调器
_ = containerProvider.Resolve<CategorySyncCoordinator>();
// 强制实例化数据字典同步协调器

View File

@@ -147,6 +147,11 @@ namespace YY.Admin.ViewModels.Control
["/xslmes/mesXslWarehouseArea"] = "WarehouseAreaListView",
["mesXslWarehouseArea"] = "WarehouseAreaListView",
// 已实现页面:密炼物料皮重策略
["MixerMaterialTareStrategyListView"] = "MixerMaterialTareStrategyListView",
["/xslmes/mesXslMixerMaterialTareStrategy"] = "MixerMaterialTareStrategyListView",
["mesXslMixerMaterialTareStrategy"] = "MixerMaterialTareStrategyListView",
// 已实现页面:打印设置
["PrintSettingsView"] = "PrintSettingsView",
["/system/printSettings"] = "PrintSettingsView",

View File

@@ -0,0 +1,295 @@
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.Core.Session;
using YY.Admin.ViewModels.RawMaterialEntry;
using YY.Admin.ViewModels.WeightRecord;
using YY.Admin.Views.RawMaterialEntry;
using YY.Admin.Views.WeightRecord;
namespace YY.Admin.ViewModels.MixerMaterialTareStrategy;
public class MixerMaterialTareStrategyEditDialogViewModel : BaseViewModel, IDialogResultable<bool>
{
private readonly IMixerMaterialTareStrategyService _tareStrategyService;
private MesXslMixerMaterialTareStrategy? _strategy;
public MesXslMixerMaterialTareStrategy? Strategy
{
get => _strategy;
set => SetProperty(ref _strategy, value);
}
public bool IsAddMode => string.IsNullOrWhiteSpace(Strategy?.Id) || (Strategy?.Id?.StartsWith("local-") ?? false);
public string DialogTitle => IsAddMode ? "新增密炼物料皮重策略" : "编辑密炼物料皮重策略";
public ObservableCollection<MesXslUnit> UnitOptions { get; } = new();
private MesMixerMaterial? _selectedMaterial;
public MesMixerMaterial? SelectedMaterial
{
get => _selectedMaterial;
set
{
SetProperty(ref _selectedMaterial, value);
RaisePropertyChanged(nameof(SelectedMaterialDisplay));
RaisePropertyChanged(nameof(HasSelectedMaterial));
if (Strategy != null && value != null)
{
Strategy.MixerMaterialId = value.Id;
Strategy.MixerMaterialName = value.MaterialName;
RaisePropertyChanged(nameof(Strategy));
}
}
}
public string SelectedMaterialDisplay => _selectedMaterial != null
? $"[{_selectedMaterial.MaterialCode}] {_selectedMaterial.MaterialName}"
: (string.IsNullOrWhiteSpace(Strategy?.MixerMaterialName) ? "请选择密炼物料" : Strategy!.MixerMaterialName!);
public bool HasSelectedMaterial =>
_selectedMaterial != null || !string.IsNullOrWhiteSpace(Strategy?.MixerMaterialId);
private MesXslSupplier? _selectedSupplier;
public MesXslSupplier? SelectedSupplier
{
get => _selectedSupplier;
set
{
SetProperty(ref _selectedSupplier, value);
RaisePropertyChanged(nameof(SelectedSupplierDisplay));
RaisePropertyChanged(nameof(HasSelectedSupplier));
if (Strategy != null && value != null)
{
Strategy.SupplierId = value.Id;
Strategy.SupplierName = value.SupplierName;
RaisePropertyChanged(nameof(Strategy));
}
}
}
public string SelectedSupplierDisplay => _selectedSupplier != null
? $"[{_selectedSupplier.SupplierCode}] {_selectedSupplier.SupplierName}"
: (string.IsNullOrWhiteSpace(Strategy?.SupplierName) ? "请选择供应商" : Strategy!.SupplierName!);
public bool HasSelectedSupplier =>
_selectedSupplier != null || !string.IsNullOrWhiteSpace(Strategy?.SupplierId);
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 OpenMaterialPickerCommand { get; }
public DelegateCommand ClearMaterialCommand { get; }
public DelegateCommand OpenSupplierPickerCommand { get; }
public DelegateCommand ClearSupplierCommand { get; }
public MixerMaterialTareStrategyEditDialogViewModel(
IMixerMaterialTareStrategyService tareStrategyService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_tareStrategyService = tareStrategyService;
SaveCommand = new DelegateCommand(async () => await SaveAsync());
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
OpenMaterialPickerCommand = new DelegateCommand(async () => await OpenMaterialPickerAsync());
ClearMaterialCommand = new DelegateCommand(ClearMaterialSelection);
OpenSupplierPickerCommand = new DelegateCommand(async () => await OpenSupplierPickerAsync());
ClearSupplierCommand = new DelegateCommand(ClearSupplierSelection);
_ = LoadUnitsAsync();
}
private async Task LoadUnitsAsync()
{
try
{
var units = await _tareStrategyService.GetUnitsAsync();
UnitOptions.Clear();
foreach (var unit in units.Where(u => !string.IsNullOrWhiteSpace(u.Id)))
UnitOptions.Add(unit);
}
catch
{
UnitOptions.Clear();
}
}
public void InitializeForAdd()
{
Strategy = new MesXslMixerMaterialTareStrategy();
_selectedMaterial = null;
_selectedSupplier = null;
RaisePropertyChanged(nameof(IsAddMode));
RaisePropertyChanged(nameof(DialogTitle));
RaisePropertyChanged(nameof(SelectedMaterialDisplay));
RaisePropertyChanged(nameof(SelectedSupplierDisplay));
RaisePropertyChanged(nameof(HasSelectedMaterial));
RaisePropertyChanged(nameof(HasSelectedSupplier));
}
public void InitializeForEdit(MesXslMixerMaterialTareStrategy strategy)
{
Strategy = new MesXslMixerMaterialTareStrategy
{
Id = strategy.Id,
TenantId = strategy.TenantId,
MixerMaterialId = strategy.MixerMaterialId,
MixerMaterialName = strategy.MixerMaterialName,
SupplierId = strategy.SupplierId,
SupplierName = strategy.SupplierName,
MaterialSpec = strategy.MaterialSpec,
TareWeight = strategy.TareWeight,
PalletWeight = strategy.PalletWeight,
UnitId = strategy.UnitId,
UnitName = strategy.UnitName,
EffectiveStartDate = strategy.EffectiveStartDate,
EffectiveEndDate = strategy.EffectiveEndDate,
MaintainBy = strategy.MaintainBy,
UpdateTime = strategy.UpdateTime
};
_selectedMaterial = null;
_selectedSupplier = null;
RaisePropertyChanged(nameof(IsAddMode));
RaisePropertyChanged(nameof(DialogTitle));
RaisePropertyChanged(nameof(SelectedMaterialDisplay));
RaisePropertyChanged(nameof(SelectedSupplierDisplay));
RaisePropertyChanged(nameof(HasSelectedMaterial));
RaisePropertyChanged(nameof(HasSelectedSupplier));
}
private async Task OpenMaterialPickerAsync()
{
RawMaterialEntryMaterialPickerDialogViewModel? pickerVm = null;
bool confirmed;
try
{
confirmed = await HandyControl.Controls.Dialog.Show<RawMaterialEntryMaterialPickerDialogView>()
.Initialize<RawMaterialEntryMaterialPickerDialogViewModel>(vm =>
{
pickerVm = vm;
vm.Initialize(Strategy?.MixerMaterialId, Strategy?.MixerMaterialName);
})
.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 (Strategy == null) return;
Strategy.MixerMaterialId = null;
Strategy.MixerMaterialName = null;
RaisePropertyChanged(nameof(Strategy));
}
private async Task OpenSupplierPickerAsync()
{
SupplierPickerDialogViewModel? pickerVm = null;
bool confirmed;
try
{
confirmed = await HandyControl.Controls.Dialog.Show<SupplierPickerDialogView>()
.Initialize<SupplierPickerDialogViewModel>(vm => pickerVm = vm)
.GetResultAsync<bool>();
}
catch { return; }
if (!confirmed || pickerVm?.SelectedSupplier == null) return;
SelectedSupplier = pickerVm.SelectedSupplier;
}
private void ClearSupplierSelection()
{
_selectedSupplier = null;
RaisePropertyChanged(nameof(SelectedSupplier));
RaisePropertyChanged(nameof(SelectedSupplierDisplay));
RaisePropertyChanged(nameof(HasSelectedSupplier));
if (Strategy == null) return;
Strategy.SupplierId = null;
Strategy.SupplierName = null;
RaisePropertyChanged(nameof(Strategy));
}
private async Task SaveAsync()
{
if (Strategy == null) return;
if (string.IsNullOrWhiteSpace(Strategy.MixerMaterialId))
{
HandyControl.Controls.MessageBox.Warning("请选择密炼物料!");
return;
}
if (string.IsNullOrWhiteSpace(Strategy.SupplierId))
{
HandyControl.Controls.MessageBox.Warning("请选择供应商!");
return;
}
if (!Strategy.TareWeight.HasValue)
{
HandyControl.Controls.MessageBox.Warning("请填写包装物重量!");
return;
}
if (Strategy.PalletWeight.HasValue && Strategy.PalletWeight.Value < 0)
{
HandyControl.Controls.MessageBox.Warning("托盘重量不能为负数!");
return;
}
if (string.IsNullOrWhiteSpace(Strategy.UnitId))
{
HandyControl.Controls.MessageBox.Warning("请选择单位!");
return;
}
if (!Strategy.EffectiveStartDate.HasValue || !Strategy.EffectiveEndDate.HasValue)
{
HandyControl.Controls.MessageBox.Warning("请填写完整的生效日期!");
return;
}
if (Strategy.EffectiveStartDate.Value.Date > Strategy.EffectiveEndDate.Value.Date)
{
HandyControl.Controls.MessageBox.Warning("生效开始日期不能晚于截止日期!");
return;
}
var selectedUnit = UnitOptions.FirstOrDefault(u => string.Equals(u.Id, Strategy.UnitId, StringComparison.OrdinalIgnoreCase));
if (selectedUnit != null)
Strategy.UnitName = selectedUnit.UnitName;
Strategy.MaintainBy = AppSession.CurrentUser?.Account ?? Strategy.MaintainBy;
Strategy.MaterialSpec = string.IsNullOrWhiteSpace(Strategy.MaterialSpec)
? null
: Strategy.MaterialSpec.Trim();
try
{
bool ok;
if (IsAddMode)
{
ok = await _tareStrategyService.AddAsync(Strategy);
if (ok) HandyControl.Controls.MessageBox.Success("新增成功!");
else { HandyControl.Controls.MessageBox.Error("新增失败!"); return; }
}
else
{
ok = await _tareStrategyService.EditAsync(Strategy);
if (!ok) { HandyControl.Controls.MessageBox.Error("编辑失败!"); return; }
}
Result = ok;
CloseAction?.Invoke();
}
catch (Exception ex)
{
HandyControl.Controls.MessageBox.Error($"操作失败:{ex.Message}");
}
}
}

View File

@@ -0,0 +1,221 @@
using HandyControl.Controls;
using HandyControl.Tools.Extension;
using Prism.Events;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Windows;
using YY.Admin.Core;
using YY.Admin.Core.Entity;
using YY.Admin.Core.Events;
using YY.Admin.Core.Helper;
using YY.Admin.Core.Services;
using YY.Admin.Views.MixerMaterialTareStrategy;
namespace YY.Admin.ViewModels.MixerMaterialTareStrategy;
public class MixerMaterialTareStrategyListViewModel : BaseViewModel
{
private readonly IMixerMaterialTareStrategyService _tareStrategyService;
private SubscriptionToken? _changedToken;
private SubscriptionToken? _syncConflictToken;
private ObservableCollection<MesXslMixerMaterialTareStrategy> _strategies = new();
public ObservableCollection<MesXslMixerMaterialTareStrategy> Strategies
{
get => _strategies;
set => SetProperty(ref _strategies, value);
}
private long _total;
public long Total { get => _total; set => SetProperty(ref _total, value); }
private int _pageNo = 1;
public int PageNo { get => _pageNo; set => SetProperty(ref _pageNo, value); }
private int _pageSize = 20;
public int PageSize { get => _pageSize; set => SetProperty(ref _pageSize, value); }
private string? _filterMixerMaterialName;
public string? FilterMixerMaterialName
{
get => _filterMixerMaterialName;
set => SetProperty(ref _filterMixerMaterialName, value);
}
private string? _filterSupplierName;
public string? FilterSupplierName
{
get => _filterSupplierName;
set => SetProperty(ref _filterSupplierName, value);
}
public DelegateCommand SearchCommand { get; }
public DelegateCommand ResetCommand { get; }
public DelegateCommand AddCommand { get; }
public DelegateCommand<MesXslMixerMaterialTareStrategy> EditCommand { get; }
public DelegateCommand<MesXslMixerMaterialTareStrategy> DeleteCommand { get; }
public DelegateCommand PrevPageCommand { get; }
public DelegateCommand NextPageCommand { get; }
public MixerMaterialTareStrategyListViewModel(
IMixerMaterialTareStrategyService tareStrategyService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_tareStrategyService = tareStrategyService;
SearchCommand = new DelegateCommand(async () => { PageNo = 1; await LoadAsync(); });
ResetCommand = new DelegateCommand(async () =>
{
FilterMixerMaterialName = null;
FilterSupplierName = null;
PageNo = 1;
await LoadAsync();
});
AddCommand = new DelegateCommand(async () => await ShowAddDialogAsync());
EditCommand = new DelegateCommand<MesXslMixerMaterialTareStrategy>(async s => await ShowEditDialogAsync(s));
DeleteCommand = new DelegateCommand<MesXslMixerMaterialTareStrategy>(async s => await DeleteAsync(s));
PrevPageCommand = new DelegateCommand(async () => { if (PageNo > 1) { PageNo--; await LoadAsync(); } });
NextPageCommand = new DelegateCommand(async () => { if ((long)PageNo * PageSize < Total) { PageNo++; await LoadAsync(); } });
_changedToken = _eventAggregator.GetEvent<MixerMaterialTareStrategyChangedEvent>()
.Subscribe(async p => await OnChangedAsync(p), ThreadOption.UIThread);
_syncConflictToken = _eventAggregator.GetEvent<SyncConflictEvent>()
.Subscribe(OnSyncConflict, ThreadOption.UIThread);
_ = InitializeAsync();
}
private async Task OnChangedAsync(MixerMaterialTareStrategyChangedPayload payload)
{
if (payload.Action == "edit" && !string.IsNullOrWhiteSpace(payload.TareStrategyId))
await RefreshSingleAsync(payload.TareStrategyId!);
else
await LoadAsync();
}
private async Task RefreshSingleAsync(string id)
{
try
{
var updated = await _tareStrategyService.GetByIdAsync(id);
if (updated == null) return;
var idx = Strategies.ToList().FindIndex(s => string.Equals(s.Id, id, StringComparison.OrdinalIgnoreCase));
if (idx >= 0) Strategies[idx] = updated;
}
catch (Exception ex)
{
Debug.WriteLine($"[密炼物料皮重策略] 单条刷新失败: {ex.Message}");
}
}
private void OnSyncConflict(SyncConflictPayload payload)
{
if (!string.Equals(payload.EntityName, "密炼物料皮重策略", StringComparison.OrdinalIgnoreCase)) return;
var parts = new List<string>();
if (payload.PushedCount > 0)
parts.Add($"已同步 {payload.PushedCount} 条本地改动到服务器");
if (payload.NewRecordsPushed > 0)
parts.Add($"已上传 {payload.NewRecordsPushed} 条本地新增记录");
if (payload.ConflictCount > 0)
parts.Add($"{payload.ConflictCount} 条记录与服务器版本冲突,已保留服务器版本");
if (parts.Count == 0) return;
var message = string.Join("\n", parts);
if (payload.ConflictCount > 0) Growl.Warning(message);
else Growl.Success(message);
}
private async Task InitializeAsync()
{
try
{
await UIHelper.WaitForRenderAsync();
await LoadAsync();
}
catch (Exception ex)
{
Debug.WriteLine($"密炼物料皮重策略列表初始化失败: {ex.Message}");
}
}
public async Task LoadAsync()
{
try
{
IsLoading = true;
var result = await _tareStrategyService.PageAsync(
PageNo, PageSize, FilterMixerMaterialName, FilterSupplierName);
Strategies = new ObservableCollection<MesXslMixerMaterialTareStrategy>(result.Records);
Total = result.Total;
}
catch (Exception ex)
{
Growl.Error($"加载密炼物料皮重策略失败:{ex.Message}");
}
finally
{
IsLoading = false;
}
}
private async Task ShowAddDialogAsync()
{
try
{
var result = await HandyControl.Controls.Dialog.Show<MixerMaterialTareStrategyEditDialogView>()
.Initialize<MixerMaterialTareStrategyEditDialogViewModel>(vm => vm.InitializeForAdd())
.GetResultAsync<bool>();
if (result) await LoadAsync();
}
catch (Exception ex)
{
Growl.Error($"打开新增对话框失败:{ex.Message}");
}
}
private async Task ShowEditDialogAsync(MesXslMixerMaterialTareStrategy strategy)
{
if (strategy == null) return;
try
{
var result = await HandyControl.Controls.Dialog.Show<MixerMaterialTareStrategyEditDialogView>()
.Initialize<MixerMaterialTareStrategyEditDialogViewModel>(vm => vm.InitializeForEdit(strategy))
.GetResultAsync<bool>();
if (result) await LoadAsync();
}
catch (Exception ex)
{
Growl.Error($"打开编辑对话框失败:{ex.Message}");
}
}
private async Task DeleteAsync(MesXslMixerMaterialTareStrategy strategy)
{
if (strategy?.Id == null) return;
var confirm = System.Windows.MessageBox.Show(
$"确定删除【{strategy.MixerMaterialName} / {strategy.SupplierName}】的包装物重量策略?此操作不可恢复!",
"确认删除", MessageBoxButton.OKCancel, MessageBoxImage.Question);
if (confirm != System.Windows.MessageBoxResult.OK) return;
var ok = await _tareStrategyService.DeleteAsync(strategy.Id);
if (ok) { Growl.Success("删除成功!"); await LoadAsync(); }
else Growl.Error("删除失败!");
}
protected override void CleanUp()
{
base.CleanUp();
if (_changedToken != null)
{
_eventAggregator.GetEvent<MixerMaterialTareStrategyChangedEvent>().Unsubscribe(_changedToken);
_changedToken = null;
}
if (_syncConflictToken != null)
{
_eventAggregator.GetEvent<SyncConflictEvent>().Unsubscribe(_syncConflictToken);
_syncConflictToken = null;
}
}
}

View File

@@ -112,6 +112,8 @@ public class RawMaterialCardEditDialogViewModel : BaseViewModel, IDialogResultab
ManufacturerMaterialName = card.ManufacturerMaterialName,
ShelfLife = card.ShelfLife,
TotalWeight = card.TotalWeight,
PackagingTare = card.PackagingTare,
PalletWeight = card.PalletWeight,
RemainingWeight = card.RemainingWeight,
RemainingQuantity = card.RemainingQuantity,
Status = card.Status,

View File

@@ -9,6 +9,7 @@ using YY.Admin.Core;
using YY.Admin.Core.Entity;
using YY.Admin.Core.Services;
using YY.Admin.Services.Service;
using YY.Admin.Services.Service.MixerMaterialTareStrategy;
using YY.Admin.Views.RawMaterialEntry;
using YY.Admin.ViewModels.WeightRecord;
using YY.Admin.Views.WeightRecord;
@@ -22,8 +23,9 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
protected IRawMaterialEntryService EntryService => _entryService;
private readonly IJeecgDictSyncService _dictSyncService;
private readonly IMixerMaterialService _mixerMaterialService;
// 加载完物料后用于回填 Edit 模式选中项
private readonly IMixerMaterialTareStrategyService _tareStrategyService;
private readonly ISupplierService _supplierService;
private bool _suppressTareStrategyRefresh;
protected string? _pendingMaterialId;
private MesXslRawMaterialEntry? _entry;
@@ -49,6 +51,8 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
RaisePropertyChanged(nameof(SelectedMaterialDisplay));
RaisePropertyChanged(nameof(HasSelectedMaterial));
_ = RefreshAllRowsTareStrategyAsync();
// 新增模式自动生成条码/批次号
if (IsAddMode && !string.IsNullOrEmpty(value.MaterialCode))
_ = AutoGenerateBarcodeAsync(value.MaterialCode);
@@ -106,6 +110,27 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
}
}
public DateTime? EntryTimeInput
{
get => Entry?.EntryTime;
set
{
if (Entry == null || Entry.EntryTime == value) return;
Entry.EntryTime = value;
RaisePropertyChanged(nameof(Entry));
_ = RefreshAllRowsTareStrategyAsync();
}
}
/// <summary>基础资料「托盘及皮重(合计)」= Σ 份数 × (包装物皮重 + 托盘重量)。</summary>
public double PalletTareTotalDisplay => SplitCodeDetails.Sum(row =>
{
var portions = row.Portions ?? 0;
var packaging = row.PackagingTare ?? 0d;
var pallet = row.PalletWeight ?? 0d;
return portions * (packaging + pallet);
});
public ObservableCollection<MesMixerMaterial> MaterialOptions { get; } = new();
public ObservableCollection<KeyValuePair<string, string>> TestResultOptions { get; } = new();
public ObservableCollection<KeyValuePair<string, string>> TestStatusOptions { get; } = new();
@@ -174,21 +199,27 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
public DelegateCommand ClearWeightRecordCommand { get; }
public DelegateCommand OpenSupplierPickerCommand { get; }
public DelegateCommand ClearSupplierCommand { get; }
/// <summary>
/// 拆码明细 - 库位选择命令弹出「库区选择」弹窗单选。CommandParameter 为当前行 RawMaterialSplitDetailItem。
/// </summary>
/// <summary>拆码明细 - 库位选择命令弹出「库区选择」弹窗单选。CommandParameter 为当前行 RawMaterialSplitDetailItem。</summary>
public DelegateCommand<RawMaterialSplitDetailItem> OpenWarehouseAreaPickerCommand { get; }
/// <summary>拆码明细 - 手动选择皮重策略。</summary>
public DelegateCommand<RawMaterialSplitDetailItem> OpenTareStrategyPickerCommand { get; }
/// <summary>拆码明细 - 清除手动策略并按规则重新自动匹配。</summary>
public DelegateCommand<RawMaterialSplitDetailItem> ResetTareStrategyCommand { get; }
public RawMaterialEntryEditDialogViewModel(
IRawMaterialEntryService entryService,
IJeecgDictSyncService dictSyncService,
IMixerMaterialService mixerMaterialService,
IMixerMaterialTareStrategyService tareStrategyService,
ISupplierService supplierService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_entryService = entryService;
_dictSyncService = dictSyncService;
_mixerMaterialService = mixerMaterialService;
_tareStrategyService = tareStrategyService;
_supplierService = supplierService;
SaveCommand = new DelegateCommand(async () => await SaveAsync());
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
ResetCommand = new DelegateCommand(InitializeForAdd);
@@ -201,6 +232,8 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
OpenSupplierPickerCommand = new DelegateCommand(async () => await OpenSupplierPickerAsync());
ClearSupplierCommand = new DelegateCommand(ClearSupplierSelection);
OpenWarehouseAreaPickerCommand = new DelegateCommand<RawMaterialSplitDetailItem>(async row => await OpenWarehouseAreaPickerAsync(row));
OpenTareStrategyPickerCommand = new DelegateCommand<RawMaterialSplitDetailItem>(async row => await OpenTareStrategyPickerAsync(row));
ResetTareStrategyCommand = new DelegateCommand<RawMaterialSplitDetailItem>(async row => await ResetTareStrategyAsync(row));
SplitCodeDetails.CollectionChanged += OnSplitCodeDetailsCollectionChanged;
_ = LoadAllAsync();
}
@@ -349,6 +382,8 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
RaisePropertyChanged(nameof(IsAddMode));
RaisePropertyChanged(nameof(DialogTitle));
RaisePropertyChanged(nameof(TotalWeightInput));
RaisePropertyChanged(nameof(EntryTimeInput));
RaisePropertyChanged(nameof(PalletTareTotalDisplay));
RaisePropertyChanged(nameof(IsSpecialAdoptionValue));
RaisePropertyChanged(nameof(SplitTotalPortionsDisplay));
RaisePropertyChanged(nameof(SplitPortionWeightDisplay));
@@ -367,8 +402,13 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
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,
ShelfLife = entry.ShelfLife, TotalWeight = entry.TotalWeight, PalletTareTotal = entry.PalletTareTotal,
TotalPortions = entry.TotalPortions,
PortionWeight = entry.PortionWeight,
PortionPackagingTare = entry.PortionPackagingTare,
PortionPalletWeight = entry.PortionPalletWeight,
PortionTareStrategyIds = entry.PortionTareStrategyIds,
PortionPackages = entry.PortionPackages,
PortionWarehouseLocations = entry.PortionWarehouseLocations,
PortionDetailIds = entry.PortionDetailIds,
PortionCardFlags = entry.PortionCardFlags,
@@ -398,6 +438,8 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
RaisePropertyChanged(nameof(IsAddMode));
RaisePropertyChanged(nameof(DialogTitle));
RaisePropertyChanged(nameof(TotalWeightInput));
RaisePropertyChanged(nameof(EntryTimeInput));
RaisePropertyChanged(nameof(PalletTareTotalDisplay));
RaisePropertyChanged(nameof(IsSpecialAdoptionValue));
RaisePropertyChanged(nameof(SplitTotalPortionsDisplay));
RaisePropertyChanged(nameof(SplitPortionWeightDisplay));
@@ -503,6 +545,7 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
Entry.BillNo = selected.BillNo;
Entry.SupplierName = selected.SenderUnit;
Entry.SupplierId = null;
await ApplySupplierFromDisplayNameAsync(selected.SenderUnit);
// 选择榜单后,自动把「剩余可入场量 = 净重 - 已入场重量」带入「总重」,
// 用户仍可手动编辑;若净重为空则保留原值,避免误清空。
if (selected.NetWeight.HasValue)
@@ -514,6 +557,76 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
}
RaisePropertyChanged(nameof(Entry));
RaisePropertyChanged(nameof(TotalWeightInput));
RaisePropertyChanged(nameof(EntryTimeInput));
RaisePropertyChanged(nameof(PalletTareTotalDisplay));
await RefreshAllRowsTareStrategyAsync();
}
/// <summary>
/// 按供应商全称/简称反查 ID榜单「发货单位」与手动填写的供应商名称均可用
/// </summary>
private async Task ApplySupplierFromDisplayNameAsync(string? displayName)
{
if (Entry == null || string.IsNullOrWhiteSpace(displayName))
{
return;
}
var supplier = await FindSupplierByDisplayNameAsync(displayName);
if (supplier == null)
{
return;
}
Entry.SupplierId = supplier.Id;
Entry.SupplierName = supplier.SupplierName
?? supplier.SupplierShortName
?? displayName.Trim();
RaisePropertyChanged(nameof(Entry));
}
private async Task<MesXslSupplier?> FindSupplierByDisplayNameAsync(string? displayName)
{
if (string.IsNullOrWhiteSpace(displayName))
{
return null;
}
var name = displayName.Trim();
try
{
var page = await _supplierService.PageAsync(1, 5000);
var exact = page.Records.FirstOrDefault(s =>
string.Equals(s.SupplierName?.Trim(), name, StringComparison.OrdinalIgnoreCase)
|| string.Equals(s.SupplierShortName?.Trim(), name, StringComparison.OrdinalIgnoreCase));
if (exact != null)
{
return exact;
}
return page.Records.FirstOrDefault(s =>
(s.SupplierName ?? "").Contains(name, StringComparison.OrdinalIgnoreCase)
|| (s.SupplierShortName ?? "").Contains(name, StringComparison.OrdinalIgnoreCase)
|| name.Contains(s.SupplierShortName ?? "", StringComparison.OrdinalIgnoreCase)
|| name.Contains(s.SupplierName ?? "", StringComparison.OrdinalIgnoreCase));
}
catch
{
return null;
}
}
/// <summary>
/// 皮重策略匹配前确保 SupplierId 已解析(榜单仅带出名称时补全 ID
/// </summary>
private async Task EnsureSupplierIdForTareMatchAsync()
{
if (Entry == null || !string.IsNullOrWhiteSpace(Entry.SupplierId))
{
return;
}
await ApplySupplierFromDisplayNameAsync(Entry.SupplierName);
}
/// <summary>
@@ -617,6 +730,7 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
Entry.SupplierId = null;
Entry.SupplierName = null;
RaisePropertyChanged(nameof(Entry));
_ = RefreshAllRowsTareStrategyAsync();
}
private async Task OpenSupplierPickerAsync()
@@ -638,6 +752,7 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
Entry.SupplierId = selected.Id;
Entry.SupplierName = selected.SupplierName;
RaisePropertyChanged(nameof(Entry));
await RefreshAllRowsTareStrategyAsync();
}
private void ClearSupplierSelection()
@@ -646,8 +761,128 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
Entry.SupplierId = null;
Entry.SupplierName = null;
RaisePropertyChanged(nameof(Entry));
_ = RefreshAllRowsTareStrategyAsync();
}
private async Task RefreshAllRowsTareStrategyAsync()
{
if (_suppressTareStrategyRefresh) return;
await EnsureSupplierIdForTareMatchAsync();
foreach (var row in SplitCodeDetails.ToList())
{
if (!row.IsManualTareStrategy)
await ApplyAutoTareStrategyToRowAsync(row);
}
}
private async Task ApplyAutoTareStrategyToRowAsync(RawMaterialSplitDetailItem row)
{
if (_suppressTareStrategyRefresh || row.IsManualTareStrategy || row.HasCard) return;
try
{
var strategies = await _tareStrategyService.GetAllForMatchAsync();
var match = MixerMaterialTareStrategyMatcher.PickBestMatch(
strategies,
Entry?.MaterialId,
Entry?.SupplierId,
Entry?.EntryTime,
row.PortionWeight);
ApplyTareStrategyToRow(row, match, manual: false);
}
catch
{
ApplyTareStrategyToRow(row, null, manual: false);
}
RaisePalletTareTotalChanged();
}
private void ApplyTareStrategyToRow(
RawMaterialSplitDetailItem row,
MesXslMixerMaterialTareStrategy? strategy,
bool manual)
{
if (strategy == null)
{
row.PackagingTare = 0d;
row.PalletWeight = 0d;
row.TareStrategyId = null;
row.TareStrategyDisplay = "未匹配(0)";
}
else
{
row.PackagingTare = strategy.TareWeight.HasValue ? (double)strategy.TareWeight.Value : 0d;
row.PalletWeight = strategy.PalletWeight.HasValue ? (double)strategy.PalletWeight.Value : 0d;
row.TareStrategyId = strategy.Id;
row.TareStrategyDisplay = BuildTareStrategyDisplay(strategy);
}
row.IsManualTareStrategy = manual && strategy != null;
}
private static string BuildTareStrategyDisplay(MesXslMixerMaterialTareStrategy strategy)
{
var spec = string.IsNullOrWhiteSpace(strategy.MaterialSpec) ? "-" : strategy.MaterialSpec;
var pkg = strategy.TareWeight?.ToString("0.##") ?? "0";
var pallet = strategy.PalletWeight?.ToString("0.##") ?? "0";
return $"规格:{spec} 包装:{pkg} 托盘:{pallet}";
}
private async Task OpenTareStrategyPickerAsync(RawMaterialSplitDetailItem? row)
{
if (row == null || row.HasCard) return;
if (string.IsNullOrWhiteSpace(Entry?.MaterialId))
{
HandyControl.Controls.MessageBox.Warning("请先选择密炼物料!");
return;
}
await EnsureSupplierIdForTareMatchAsync();
if (string.IsNullOrWhiteSpace(Entry?.SupplierId))
{
HandyControl.Controls.MessageBox.Warning("未能匹配到供应商档案,请手动选择供应商,或检查榜单发货单位是否与供应商简称/全称一致!");
return;
}
TareStrategyPickerDialogViewModel? pickerVm = null;
bool confirmed;
try
{
confirmed = await SuspendEmbeddedPrintPreviewAirspaceWhileAsync(async () =>
{
return await HandyControl.Controls.Dialog.Show<TareStrategyPickerDialogView>()
.Initialize<TareStrategyPickerDialogViewModel>(vm =>
{
pickerVm = vm;
vm.Initialize(Entry!.MaterialId, Entry.SupplierId, Entry.EntryTime, row.PortionWeight, row.TareStrategyId);
})
.GetResultAsync<bool>();
});
}
catch { return; }
if (!confirmed) return;
if (pickerVm?.SelectedStrategy == null)
{
row.IsManualTareStrategy = false;
await ApplyAutoTareStrategyToRowAsync(row);
return;
}
ApplyTareStrategyToRow(row, pickerVm.SelectedStrategy, manual: true);
RaisePalletTareTotalChanged();
}
private async Task ResetTareStrategyAsync(RawMaterialSplitDetailItem? row)
{
if (row == null || row.HasCard) return;
row.IsManualTareStrategy = false;
await ApplyAutoTareStrategyToRowAsync(row);
}
private void RaisePalletTareTotalChanged() => RaisePropertyChanged(nameof(PalletTareTotalDisplay));
private void RecalculateShelfLife(MesMixerMaterial? material)
{
if (Entry == null || material?.ShelfLifeDays == null || material.ShelfLifeDays <= 0)
@@ -719,63 +954,68 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
protected void InitializeSplitCodeDetailsFromEntry()
{
SplitCodeDetails.Clear();
// 六个字段都是按拆码明细多行拼接的字符串(末尾带 /),解析回明细列表
var portionsArr = SplitJoinedValues(Entry?.TotalPortions);
var weightArr = SplitJoinedValues(Entry?.PortionWeight);
var packagesArr = SplitJoinedValues(Entry?.PortionPackages);
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++)
_suppressTareStrategyRefresh = true;
try
{
var locationFromArr = GetAt(locationsArr, i);
// 兼容历史数据:明细库位拼接字段为空时,首行回退到 Entry.WarehouseLocation
// (早期版本里整票级库位曾被反写过,避免老记录打开后明细全空)
var locationFallback = (i == 0 && string.IsNullOrWhiteSpace(locationFromArr))
? Entry?.WarehouseLocation
: locationFromArr;
SplitCodeDetails.Clear();
var item = new RawMaterialSplitDetailItem
var portionsArr = SplitJoinedValues(Entry?.TotalPortions);
var weightArr = SplitJoinedValues(Entry?.PortionWeight);
var packagingTareArr = SplitJoinedValues(Entry?.PortionPackagingTare);
var palletWeightArr = SplitJoinedValues(Entry?.PortionPalletWeight);
var strategyIdsArr = SplitJoinedValues(Entry?.PortionTareStrategyIds);
var packagesArr = SplitJoinedValues(Entry?.PortionPackages);
var locationsArr = SplitJoinedValues(Entry?.PortionWarehouseLocations);
var idsArr = SplitJoinedValues(Entry?.PortionDetailIds);
var cardFlagsArr = SplitJoinedValues(Entry?.PortionCardFlags);
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++)
{
Portions = TryParseInt(GetAt(portionsArr, i)),
PortionWeight = TryParseDouble(GetAt(weightArr, i)),
PortionPackages = TryParseInt(GetAt(packagesArr, i)),
WarehouseLocation = locationFallback,
};
// 仅当后端已持久化了该行 ID 时才覆盖默认值,确保跨次保持稳定;
// 历史记录无 ID 字段时使用构造期生成的新 GUID之后保存会回填
var existingId = GetAt(idsArr, i);
if (!string.IsNullOrWhiteSpace(existingId))
{
item.Id = existingId;
var locationFromArr = GetAt(locationsArr, i);
var locationFallback = (i == 0 && string.IsNullOrWhiteSpace(locationFromArr))
? Entry?.WarehouseLocation
: locationFromArr;
var strategyId = GetAt(strategyIdsArr, i);
var item = new RawMaterialSplitDetailItem
{
Portions = TryParseInt(GetAt(portionsArr, i)),
PortionWeight = TryParseDouble(GetAt(weightArr, i)),
PortionPackages = TryParseInt(GetAt(packagesArr, i)),
WarehouseLocation = locationFallback,
PackagingTare = TryParseDouble(GetAt(packagingTareArr, i)) ?? 0d,
PalletWeight = TryParseDouble(GetAt(palletWeightArr, i)) ?? 0d,
TareStrategyId = strategyId,
IsManualTareStrategy = !string.IsNullOrWhiteSpace(strategyId),
};
item.TareStrategyDisplay = string.IsNullOrWhiteSpace(strategyId)
? "未匹配(0)"
: $"策略ID:{strategyId}";
var existingId = GetAt(idsArr, i);
if (!string.IsNullOrWhiteSpace(existingId))
item.Id = existingId;
var flagAt = GetAt(cardFlagsArr, i);
if (!string.IsNullOrWhiteSpace(flagAt))
item.HasCard = string.Equals(flagAt, "1", StringComparison.Ordinal);
else if (fallbackToPrintFlag && !string.IsNullOrWhiteSpace(existingId))
item.HasCard = true;
SplitCodeDetails.Add(item);
}
// 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));
RaisePalletTareTotalChanged();
}
finally
{
_suppressTareStrategyRefresh = false;
}
RaisePropertyChanged(nameof(SplitCodeTableHeight));
}
private static string[] SplitJoinedValues(string? value)
@@ -798,8 +1038,10 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
private void AddSplitDetailRow()
{
SplitCodeDetails.Add(new RawMaterialSplitDetailItem());
var row = new RawMaterialSplitDetailItem();
SplitCodeDetails.Add(row);
RaisePropertyChanged(nameof(SplitCodeTableHeight));
_ = ApplyAutoTareStrategyToRowAsync(row);
}
private void RemoveSplitDetailRow(RawMaterialSplitDetailItem? item)
@@ -841,32 +1083,52 @@ 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 == nameof(RawMaterialSplitDetailItem.PortionWeight)
&& sender is RawMaterialSplitDetailItem weightRow)
{
// 清空每份重量后,若份数已填则立即按公式重算(配合 PortionWeightText 空值绑定)
if (!weightRow.PortionWeight.HasValue)
{
RecalculatePortionWeightForRow(weightRow);
}
if (!weightRow.IsManualTareStrategy && !weightRow.HasCard)
{
_ = ApplyAutoTareStrategyToRowAsync(weightRow);
}
}
if (e.PropertyName is nameof(RawMaterialSplitDetailItem.Portions)
or nameof(RawMaterialSplitDetailItem.PortionWeight)
or nameof(RawMaterialSplitDetailItem.PortionPackages))
or nameof(RawMaterialSplitDetailItem.PortionPackages)
or nameof(RawMaterialSplitDetailItem.PackagingTare)
or nameof(RawMaterialSplitDetailItem.PalletWeight))
{
RaiseSplitDisplayPropertyChanged();
}
}
/// <summary>
/// 拆码明细某行的「份数」变化时,按公式重算该行「每份重量」:
/// 拆码明细某行的「份数」变化时,仅当该行「每份重量」为空时按公式重算
/// 公式:每份重量 = (总重 - 其他行 Σ份数×每份重量) / 当前行份数
/// — 用户单独手改「每份重量」不触发
/// — 已填写「每份重量」后再改份数,不覆盖用户输入
/// — 总重为空 / ≤0、当前份数 ≤0、剩余总重 ≤0 时跳过;
/// — 结果四舍五入到两位小数
/// — 写入 row.PortionWeight 会触发 PropertyChanged(PortionWeight)不会再次进入本方法PropertyName 已不是 Portions不会形成循环。
/// — 结果四舍五入到两位小数
/// </summary>
private void RecalculatePortionWeightForRow(RawMaterialSplitDetailItem row)
{
if (row.PortionWeight.HasValue)
{
return;
}
if (Entry?.TotalWeight is not { } total || total <= 0d)
{
return;
@@ -898,6 +1160,7 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
RaisePropertyChanged(nameof(SplitTotalPortionsDisplay));
RaisePropertyChanged(nameof(SplitPortionWeightDisplay));
RaisePropertyChanged(nameof(SplitPortionPackagesDisplay));
RaisePalletTareTotalChanged();
}
private string JoinSplitValue(Func<RawMaterialSplitDetailItem, string?> selector, bool withTrailingSlash)
@@ -938,6 +1201,11 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
Entry.TotalPortions = JoinSplitValue(it => it.Portions?.ToString(CultureInfo.InvariantCulture), true);
Entry.PortionWeight = JoinSplitValue(it => FormatNullableDecimal(it.PortionWeight), true);
Entry.PortionPackagingTare = JoinSplitValue(it => FormatNullableDecimal(it.PackagingTare), true);
Entry.PortionPalletWeight = JoinSplitValue(it => FormatNullableDecimal(it.PalletWeight), true);
Entry.PortionTareStrategyIds = JoinSplitValue(
it => it.IsManualTareStrategy && !string.IsNullOrWhiteSpace(it.TareStrategyId) ? it.TareStrategyId : null,
true);
Entry.PortionPackages = JoinSplitValue(it => it.PortionPackages?.ToString(CultureInfo.InvariantCulture), true);
Entry.PortionWarehouseLocations = JoinSplitValue(it => string.IsNullOrWhiteSpace(it.WarehouseLocation) ? null : it.WarehouseLocation.Trim(), true);
// 持久化每行 GUID行序与上方四个字段对齐JoinSplitValue 会过滤空,但 Id 在构造时即生成不会为空。
@@ -947,6 +1215,7 @@ public class RawMaterialEntryEditDialogViewModel : BaseViewModel, IDialogResulta
// 「生成原材料卡片」只对 HasCard=false 的行加卡 → 必须把真实的 HasCard 持久化,
// 否则重新加载后无法区分「已生成」与「保存后新增的未生成行」。
Entry.PortionCardFlags = JoinSplitValue(it => it.HasCard ? "1" : "0", true);
Entry.PalletTareTotal = PalletTareTotalDisplay;
}
private double CalculateSplitCodeTableHeight()
@@ -983,7 +1252,37 @@ public class RawMaterialSplitDetailItem : BindableBase
public double? PortionWeight
{
get => _portionWeight;
set => SetProperty(ref _portionWeight, value);
set
{
if (SetProperty(ref _portionWeight, value))
{
RaisePropertyChanged(nameof(PortionWeightText));
}
}
}
/// <summary>
/// 每份重量文本(供 TextBox 绑定;空字符串表示未填写,避免 double? 直接绑定无法清空)。
/// </summary>
public string PortionWeightText
{
get => _portionWeight.HasValue
? _portionWeight.Value.ToString("0.##", CultureInfo.InvariantCulture)
: string.Empty;
set
{
var text = value?.Trim() ?? string.Empty;
if (string.IsNullOrEmpty(text))
{
PortionWeight = null;
return;
}
if (double.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var parsed))
{
PortionWeight = parsed;
}
}
}
private int? _portionPackages;
@@ -1000,6 +1299,42 @@ public class RawMaterialSplitDetailItem : BindableBase
set => SetProperty(ref _warehouseLocation, value);
}
private double? _packagingTare;
public double? PackagingTare
{
get => _packagingTare;
set => SetProperty(ref _packagingTare, value);
}
private double? _palletWeight;
public double? PalletWeight
{
get => _palletWeight;
set => SetProperty(ref _palletWeight, value);
}
private string? _tareStrategyId;
public string? TareStrategyId
{
get => _tareStrategyId;
set => SetProperty(ref _tareStrategyId, value);
}
private string? _tareStrategyDisplay = "未匹配(0)";
public string? TareStrategyDisplay
{
get => _tareStrategyDisplay;
set => SetProperty(ref _tareStrategyDisplay, value);
}
private bool _isManualTareStrategy;
/// <summary>用户手动选择皮重策略后为 true自动匹配不再覆盖。</summary>
public bool IsManualTareStrategy
{
get => _isManualTareStrategy;
set => SetProperty(ref _isManualTareStrategy, value);
}
/// <summary>
/// 该行是否已生成原材料卡片(运行时状态,不直接持久化)。
/// 加载时根据 Entry.PrintFlag + Id 是否来自持久化的 PortionDetailIds 推断;

View File

@@ -242,13 +242,15 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
IRawMaterialEntryService entryService,
IJeecgDictSyncService dictSyncService,
IMixerMaterialService mixerMaterialService,
IMixerMaterialTareStrategyService tareStrategyService,
ISupplierService supplierService,
IRawMaterialCardService rawMaterialCardService,
IPrintDotService printDotService,
IPrintBizTemplateBindService printBizTemplateBindService,
IPrintTemplateService printTemplateService,
IContainerExtension container,
IRegionManager regionManager)
: base(entryService, dictSyncService, mixerMaterialService, container, regionManager)
: base(entryService, dictSyncService, mixerMaterialService, tareStrategyService, supplierService, container, regionManager)
{
_rawMaterialCardService = rawMaterialCardService;
_printDotService = printDotService;
@@ -802,6 +804,8 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
ManufacturerMaterialName = Entry.ManufacturerMaterialName,
ShelfLife = Entry.ShelfLife,
TotalWeight = detail.PortionWeight.HasValue ? (decimal?)detail.PortionWeight.Value : null,
PackagingTare = detail.PackagingTare.HasValue ? (decimal?)detail.PackagingTare.Value : null,
PalletWeight = detail.PalletWeight.HasValue ? (decimal?)detail.PalletWeight.Value : null,
RemainingWeight = detail.PortionWeight.HasValue ? (decimal?)detail.PortionWeight.Value : null,
RemainingQuantity = detail.PortionPackages,
WarehouseArea = detail.WarehouseLocation,
@@ -845,9 +849,10 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
RaisePropertyChanged(nameof(CanGenerateCards));
RaisePropertyChanged(nameof(CanResplit));
string? printError = null;
if (newCardCount > 0 && generatedCards.Count > 0)
{
var printError = await PrintGeneratedRawMaterialCardsAsync(selectedPrinterName, generatedCards);
printError = await PrintGeneratedRawMaterialCardsAsync(selectedPrinterName, generatedCards);
if (!string.IsNullOrWhiteSpace(printError))
{
HandyControl.Controls.MessageBox.Warning($"卡片已生成 {newCardCount} 张,但打印存在异常:{printError}");
@@ -855,7 +860,12 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
}
if (failCount == 0)
HandyControl.Controls.MessageBox.Success($"已生成并打印 {newCardCount} 张原材料卡片(累计 {alreadyGenerated + newCardCount} 张),打印状态已更新为「已打印」!");
{
if (string.IsNullOrWhiteSpace(printError))
HandyControl.Controls.MessageBox.Success($"已生成并打印 {newCardCount} 张原材料卡片(累计 {alreadyGenerated + newCardCount} 张),打印状态已更新为「已打印」!");
else
HandyControl.Controls.MessageBox.Success($"已生成 {newCardCount} 张原材料卡片(累计 {alreadyGenerated + newCardCount} 张),打印状态已更新;请检查打印机后补打标签。");
}
else
HandyControl.Controls.MessageBox.Warning($"本次共尝试生成 {newCardCount + failCount} 张,成功 {newCardCount} 张,失败 {failCount} 张。失败的行未标记为「已打印」,可检查网络后再次点击「生成原材料卡片」重试。");
}
@@ -950,6 +960,8 @@ public class RawMaterialEntryOperationViewModel : RawMaterialEntryEditDialogView
ManufacturerMaterialName = entry.ManufacturerMaterialName,
ShelfLife = entry.ShelfLife,
TotalWeight = detail.PortionWeight.HasValue ? (decimal?)detail.PortionWeight.Value : null,
PackagingTare = detail.PackagingTare.HasValue ? (decimal?)detail.PackagingTare.Value : null,
PalletWeight = detail.PalletWeight.HasValue ? (decimal?)detail.PalletWeight.Value : null,
RemainingWeight = detail.PortionWeight.HasValue ? (decimal?)detail.PortionWeight.Value : null,
RemainingQuantity = detail.PortionPackages,
WarehouseArea = detail.WarehouseLocation,

View File

@@ -0,0 +1,110 @@
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.MixerMaterialTareStrategy;
namespace YY.Admin.ViewModels.RawMaterialEntry;
/// <summary>
/// 拆码明细手动选择「密炼物料皮重策略」弹窗。
/// </summary>
public class TareStrategyPickerDialogViewModel : BaseViewModel, IDialogResultable<bool>
{
private readonly IMixerMaterialTareStrategyService _tareStrategyService;
private string? _materialId;
private string? _supplierId;
private DateTime? _entryDate;
private double? _portionWeight;
public ObservableCollection<MesXslMixerMaterialTareStrategy> Records { get; } = new();
private MesXslMixerMaterialTareStrategy? _selectedStrategy;
public MesXslMixerMaterialTareStrategy? SelectedStrategy
{
get => _selectedStrategy;
set
{
SetProperty(ref _selectedStrategy, value);
ConfirmCommand.RaiseCanExecuteChanged();
RaisePropertyChanged(nameof(SelectedStrategyDisplay));
}
}
public string SelectedStrategyDisplay => _selectedStrategy == null
? "请选中一条策略后确认,或点「使用自动匹配」"
: $"规格:{_selectedStrategy.MaterialSpec ?? "-"} 包装物:{_selectedStrategy.TareWeight:0.###} 托盘:{_selectedStrategy.PalletWeight:0.###}";
private bool _result;
public bool Result { get => _result; set => SetProperty(ref _result, value); }
public Action? CloseAction { get; set; }
public DelegateCommand ConfirmCommand { get; }
public DelegateCommand UseAutoMatchCommand { get; }
public DelegateCommand CancelCommand { get; }
public TareStrategyPickerDialogViewModel(
IMixerMaterialTareStrategyService tareStrategyService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_tareStrategyService = tareStrategyService;
ConfirmCommand = new DelegateCommand(ConfirmSelection, () => SelectedStrategy != null);
UseAutoMatchCommand = new DelegateCommand(UseAutoMatch);
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
}
public void Initialize(
string? materialId,
string? supplierId,
DateTime? entryDate,
double? portionWeight,
string? currentStrategyId)
{
_materialId = materialId;
_supplierId = supplierId;
_entryDate = entryDate;
_portionWeight = portionWeight;
_ = LoadAsync(currentStrategyId);
}
private async Task LoadAsync(string? currentStrategyId)
{
try
{
var all = await _tareStrategyService.GetAllForMatchAsync();
var filtered = MixerMaterialTareStrategyMatcher.FilterCandidates(
all, _materialId, _supplierId, _entryDate, _portionWeight);
Records.Clear();
foreach (var item in filtered)
Records.Add(item);
if (!string.IsNullOrWhiteSpace(currentStrategyId))
{
SelectedStrategy = Records.FirstOrDefault(r =>
string.Equals(r.Id, currentStrategyId, StringComparison.OrdinalIgnoreCase))
?? all.FirstOrDefault(r => string.Equals(r.Id, currentStrategyId, StringComparison.OrdinalIgnoreCase));
}
}
catch
{
Records.Clear();
}
}
private void ConfirmSelection()
{
Result = true;
CloseAction?.Invoke();
}
private void UseAutoMatch()
{
SelectedStrategy = null;
Result = true;
CloseAction?.Invoke();
}
}

View File

@@ -0,0 +1,163 @@
<UserControl x:Class="YY.Admin.Views.MixerMaterialTareStrategy.MixerMaterialTareStrategyEditDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d"
Width="760"
MinHeight="560">
<Grid Background="{DynamicResource ThirdlyRegionBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<hc:SimplePanel Margin="20">
<TextBlock FontSize="18" Foreground="{DynamicResource PrimaryTextBrush}"
Text="{Binding DialogTitle}" HorizontalAlignment="Left" VerticalAlignment="Top"/>
<Button Width="22" Height="22" Command="hc:ControlCommands.Close"
Style="{StaticResource ButtonIcon}" Foreground="{DynamicResource PrimaryBrush}"
hc:IconElement.Geometry="{StaticResource ErrorGeometry}"
Padding="0" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,4,4,0"/>
</hc:SimplePanel>
<hc:ScrollViewer Grid.Row="1" IsInertiaEnabled="True">
<StackPanel Margin="20,0,20,0">
<hc:Row Gutter="10">
<hc:Col Span="24">
<Grid Margin="0,0,0,16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<hc:TextBox Grid.Column="0"
Text="{Binding SelectedMaterialDisplay, Mode=OneWay}"
IsReadOnly="True"
hc:InfoElement.Title="密炼物料"
hc:InfoElement.TitleWidth="90"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"/>
<Button Grid.Column="1" Margin="8,0,0,0" Content="选择" Width="70"
Command="{Binding OpenMaterialPickerCommand}" Style="{StaticResource ButtonPrimary}"/>
<Button Grid.Column="2" Margin="8,0,0,0" Content="清除" Width="70"
Command="{Binding ClearMaterialCommand}" Style="{StaticResource ButtonDefault}"/>
</Grid>
</hc:Col>
<hc:Col Span="24">
<Grid Margin="0,0,0,16">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<hc:TextBox Grid.Column="0"
Text="{Binding SelectedSupplierDisplay, Mode=OneWay}"
IsReadOnly="True"
hc:InfoElement.Title="供应商"
hc:InfoElement.TitleWidth="90"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"/>
<Button Grid.Column="1" Margin="8,0,0,0" Content="选择" Width="70"
Command="{Binding OpenSupplierPickerCommand}" Style="{StaticResource ButtonPrimary}"/>
<Button Grid.Column="2" Margin="8,0,0,0" Content="清除" Width="70"
Command="{Binding ClearSupplierCommand}" Style="{StaticResource ButtonDefault}"/>
</Grid>
</hc:Col>
<hc:Col Span="24">
<StackPanel Margin="0,0,0,16">
<hc:TextBox Text="{Binding Strategy.MaterialSpec, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="物料规格"
hc:InfoElement.TitleWidth="90"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="请输入物料规格"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,4"/>
<TextBlock Text="同一供应商/密炼物料下,不同规格可分别维护;规格相同且生效日期重叠时不允许重复"
Foreground="{DynamicResource SecondaryTextBrush}"
FontSize="12"
TextWrapping="Wrap"
Margin="90,0,0,0"/>
</StackPanel>
</hc:Col>
<hc:Col Span="12">
<hc:NumericUpDown Value="{Binding Strategy.TareWeight}"
Minimum="0"
DecimalPlaces="4"
Style="{StaticResource NumericUpDownPlus}"
hc:InfoElement.Title="包装物重量"
hc:InfoElement.TitleWidth="90"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
hc:InfoElement.Placeholder="请输入包装物重量"
Margin="0,0,0,16"/>
</hc:Col>
<hc:Col Span="12">
<hc:NumericUpDown Value="{Binding Strategy.PalletWeight}"
Minimum="0"
DecimalPlaces="4"
Style="{StaticResource NumericUpDownPlus}"
hc:InfoElement.Title="托盘重量"
hc:InfoElement.TitleWidth="90"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="请输入托盘重量"
Margin="0,0,0,16"/>
</hc:Col>
<hc:Col Span="12">
<hc:ComboBox SelectedValuePath="Id"
DisplayMemberPath="UnitName"
ItemsSource="{Binding UnitOptions}"
SelectedValue="{Binding Strategy.UnitId, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="单位"
hc:InfoElement.TitleWidth="90"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,16"/>
</hc:Col>
<hc:Col Span="12">
<hc:DatePicker SelectedDate="{Binding Strategy.EffectiveStartDate, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="生效开始"
hc:InfoElement.TitleWidth="90"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
Margin="0,0,0,16"/>
</hc:Col>
<hc:Col Span="12">
<hc:DatePicker SelectedDate="{Binding Strategy.EffectiveEndDate, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="生效截止"
hc:InfoElement.TitleWidth="90"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
Margin="0,0,0,16"/>
</hc:Col>
</hc:Row>
</StackPanel>
</hc:ScrollViewer>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center" Margin="20">
<Button Content="取消" Command="{Binding CancelCommand}" Style="{StaticResource ButtonDefault}" Margin="0,0,15,0" Width="100"/>
<Button Content="确定" Command="{Binding SaveCommand}" Style="{StaticResource ButtonPrimary}" Width="100"/>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,11 @@
using System.Windows.Controls;
namespace YY.Admin.Views.MixerMaterialTareStrategy;
public partial class MixerMaterialTareStrategyEditDialogView : UserControl
{
public MixerMaterialTareStrategyEditDialogView()
{
InitializeComponent();
}
}

View File

@@ -0,0 +1,138 @@
<UserControl x:Class="YY.Admin.Views.MixerMaterialTareStrategy.MixerMaterialTareStrategyListView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
mc:Ignorable="d">
<Grid Style="{StaticResource BaseViewStyle}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.Row="0" CornerRadius="4" Margin="0 0 -10 0">
<hc:Row>
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
<hc:TextBox Text="{Binding FilterMixerMaterialName, UpdateSourceTrigger=PropertyChanged}"
Margin="0 0 10 10"
hc:InfoElement.Title="密炼物料"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.TitleWidth="65"
hc:InfoElement.Placeholder="请输入密炼物料名称"
hc:InfoElement.ShowClearButton="True"/>
</hc:Col>
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=6, Xl=4}">
<hc:TextBox Text="{Binding FilterSupplierName, UpdateSourceTrigger=PropertyChanged}"
Margin="0 0 10 10"
hc:InfoElement.Title="供应商"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.TitleWidth="65"
hc:InfoElement.Placeholder="请输入供应商名称"
hc:InfoElement.ShowClearButton="True"/>
</hc:Col>
</hc:Row>
</Border>
<Border Grid.Row="1" Margin="0,10">
<hc:UniformSpacingPanel Spacing="10">
<Button Style="{StaticResource ButtonPrimary}" Command="{Binding SearchCommand}">
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="Search"/>
<TextBlock Text="搜索" Style="{StaticResource IconButtonStyle}"/>
</StackPanel>
</Button>
<Button Style="{StaticResource ButtonDefault}" Command="{Binding ResetCommand}">
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="Refresh"/>
<TextBlock Text="重置" Style="{StaticResource IconButtonStyle}"/>
</StackPanel>
</Button>
<Button Style="{StaticResource ButtonSuccess}" Command="{Binding AddCommand}">
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="Plus"/>
<TextBlock Text="新增" Style="{StaticResource IconButtonStyle}"/>
</StackPanel>
</Button>
</hc:UniformSpacingPanel>
</Border>
<DataGrid Grid.Row="2"
ItemsSource="{Binding Strategies}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserAddRows="False"
SelectionMode="Extended"
SelectionUnit="FullRow"
RowHeaderWidth="55"
GridLinesVisibility="Horizontal"
HorizontalGridLinesBrush="#FFEDEDED"
VerticalGridLinesBrush="Transparent"
HeadersVisibility="All"
ColumnHeaderStyle="{StaticResource CusDataGridColumnHeaderStyle}"
Style="{StaticResource CusDataGridStyle}"
hc:DataGridAttach.ShowSelectAllButton="True"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<DataGrid.RowHeaderTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridRow}}"/>
</DataTemplate>
</DataGrid.RowHeaderTemplate>
<DataGrid.Columns>
<DataGridTextColumn Header="密炼物料" Binding="{Binding MixerMaterialName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
<DataGridTextColumn Header="供应商" Binding="{Binding SupplierName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
<DataGridTextColumn Header="物料规格" Binding="{Binding MaterialSpec}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="120"/>
<DataGridTextColumn Header="包装物重量" Binding="{Binding TareWeight}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
<DataGridTextColumn Header="托盘重量" Binding="{Binding PalletWeight}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
<DataGridTextColumn Header="单位" Binding="{Binding UnitName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="80"/>
<DataGridTextColumn Header="生效开始" Binding="{Binding EffectiveStartDateText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="110"/>
<DataGridTextColumn Header="生效截止" Binding="{Binding EffectiveEndDateText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="110"/>
<DataGridTextColumn Header="维护人" Binding="{Binding MaintainBy}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
<DataGridTemplateColumn Header="操作" Width="140" CellStyle="{StaticResource CusOperDataGridCellStyle}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<hc:UniformSpacingPanel Spacing="5">
<Border Style="{DynamicResource DataGridOpeButtonStyle}">
<Border.InputBindings>
<MouseBinding Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}" MouseAction="LeftClick"/>
</Border.InputBindings>
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="SquareEditOutline" VerticalAlignment="Center"/>
<TextBlock Text="修改" VerticalAlignment="Center"/>
</StackPanel>
</Border>
<Border Style="{DynamicResource DataGridOpeButtonStyle}">
<Border.InputBindings>
<MouseBinding Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource AncestorType=DataGrid}}"
CommandParameter="{Binding}" MouseAction="LeftClick"/>
</Border.InputBindings>
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="TrashCanOutline" VerticalAlignment="Center"/>
<TextBlock Text="删除" VerticalAlignment="Center"/>
</StackPanel>
</Border>
</hc:UniformSpacingPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
<TextBlock Text="{Binding Total, StringFormat=共 {0} 条}" VerticalAlignment="Center" Margin="0,0,16,0"
Foreground="{DynamicResource SecondaryTextBrush}"/>
<Button Content="上一页" Command="{Binding PrevPageCommand}" Style="{StaticResource ButtonDefault}" Margin="0,0,4,0" Width="80"/>
<TextBlock Text="{Binding PageNo, StringFormat=第 {0} 页}" VerticalAlignment="Center" Margin="8,0"
Foreground="{DynamicResource PrimaryTextBrush}"/>
<Button Content="下一页" Command="{Binding NextPageCommand}" Style="{StaticResource ButtonDefault}" Width="80"/>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,11 @@
using System.Windows.Controls;
namespace YY.Admin.Views.MixerMaterialTareStrategy;
public partial class MixerMaterialTareStrategyListView : UserControl
{
public MixerMaterialTareStrategyListView()
{
InitializeComponent();
}
}

View File

@@ -467,20 +467,20 @@ public partial class PrintPreviewWindow : HandyControl.Controls.Window
{
if (_printDotService == null)
{
HandyControl.Controls.Growl.Warning("PrintDot 服务不可用");
HandyControl.Controls.MessageBox.Warning("PrintDot 服务不可用");
return;
}
if (string.IsNullOrWhiteSpace(_templateJson) || _templateJson == "{}")
{
HandyControl.Controls.Growl.Warning("当前模板没有可打印内容");
HandyControl.Controls.MessageBox.Warning("当前模板没有可打印内容");
return;
}
var selected = PrinterCombo.SelectedItem as PrintDotPrinter;
if (selected == null || string.IsNullOrWhiteSpace(selected.Name))
{
HandyControl.Controls.Growl.Warning("请先选择打印机");
HandyControl.Controls.MessageBox.Warning("请先选择打印机");
return;
}
@@ -521,13 +521,13 @@ public partial class PrintPreviewWindow : HandyControl.Controls.Window
await _printDotService.PrintAsync(selected.Name, pdfBase64, jobName, copies: 1);
SetStatus("打印任务已发送");
HandyControl.Controls.Growl.Success("打印任务已发送至 PrintDot");
HandyControl.Controls.MessageBox.Success("打印任务已发送至 PrintDot");
}
catch (Exception ex)
{
// 顶部状态栏单行显示首行,完整多行处理步骤在 Growl 弹窗中展示
// 独立预览窗无 Growl 面板,用状态栏 + MessageBox 展示错误,避免 NullReferenceException
SetStatus($"打印失败:{ex.Message}");
HandyControl.Controls.Growl.Error($"打印失败:{ex.Message}");
HandyControl.Controls.MessageBox.Error($"打印失败:{ex.Message}");
}
finally
{

View File

@@ -127,6 +127,8 @@
<DataGridTextColumn Header="厂家物料名称" Binding="{Binding ManufacturerMaterialName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="130"/>
<DataGridTextColumn Header="保质期" Binding="{Binding ShelfLife}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="90"/>
<DataGridTextColumn Header="总重(kg)" Binding="{Binding TotalWeight, StringFormat=N3}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="90"/>
<DataGridTextColumn Header="包装物皮重" Binding="{Binding PackagingTare, StringFormat=N3}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="95"/>
<DataGridTextColumn Header="托盘重量" Binding="{Binding PalletWeight, StringFormat=N3}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="90"/>
<DataGridTextColumn Header="剩余重量" Binding="{Binding RemainingWeight, StringFormat=N3}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="90"/>
<DataGridTextColumn Header="剩余数量" Binding="{Binding RemainingQuantity}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="80"/>
<DataGridTextColumn Header="状态" Binding="{Binding StatusText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="70"/>

View File

@@ -124,6 +124,7 @@
<DataGridTextColumn Header="供料客户" Binding="{Binding SupplyCustomer}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="120"/>
<DataGridTextColumn Header="供应商名称" Binding="{Binding SupplierName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
<DataGridTextColumn Header="总重(KG)" Binding="{Binding TotalWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="90"/>
<DataGridTextColumn Header="托盘及皮重(合计)" Binding="{Binding PalletTareTotal, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="120"/>
<DataGridTextColumn Header="总份数" Binding="{Binding TotalPortions}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="70"/>
<DataGridTextColumn Header="检测结果" Binding="{Binding TestResultText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="85"/>
<DataGridTextColumn Header="检测状态" Binding="{Binding TestStatusText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="85"/>

View File

@@ -132,6 +132,7 @@
<RowDefinition Height="36"/> <!-- 厂家物料名称 / 保质期 -->
<RowDefinition Height="40"/> <!-- 总重 / 总份数 — 多 4px 给 NumericUpDown 上下间距,否则 spinner 箭头会被裁切 -->
<RowDefinition Height="36"/> <!-- 每份总重 / 每份包数 -->
<RowDefinition Height="36"/> <!-- 托盘及皮重(合计) -->
<RowDefinition Height="36"/> <!-- 库位 / 卸货人 -->
<RowDefinition Height="60"/> <!-- 备注 -->
</Grid.RowDefinitions>
@@ -205,7 +206,7 @@
<Border Grid.Row="1" Grid.Column="3"
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource BorderBrush}" Padding="4,3">
<!-- 自定义日期时间选择器:日历 + 时/分/秒 三列 + 此刻/确定 -->
<ctrls:DateTimeListPicker SelectedDateTime="{Binding Entry.EntryTime, Mode=TwoWay}"
<ctrls:DateTimeListPicker SelectedDateTime="{Binding EntryTimeInput, Mode=TwoWay}"
DateTimeFormat="yyyy-MM-dd HH:mm:ss"
Placeholder="请选择入场时间"/>
</Border>
@@ -458,14 +459,30 @@
</Grid>
</Border>
<!-- ===== Row 7: 库位 / 卸货人 ===== -->
<!-- ===== Row 7: 托盘及皮重(合计) ===== -->
<Border Grid.Row="7" Grid.Column="0"
Background="{DynamicResource SecondaryRegionBrush}"
BorderThickness="0,0,1,1" BorderBrush="{DynamicResource BorderBrush}">
<TextBlock Text="托盘及皮重(合计)" HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="11" Foreground="{DynamicResource PrimaryTextBrush}"
TextWrapping="Wrap" TextAlignment="Center"/>
</Border>
<Border Grid.Row="7" Grid.Column="1" Grid.ColumnSpan="3"
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource BorderBrush}" Padding="6,0">
<TextBlock Text="{Binding PalletTareTotalDisplay, StringFormat={}{0:0.##}}"
VerticalAlignment="Center" FontSize="13"
Foreground="{DynamicResource SecondaryTextBrush}"
ToolTip="由拆码明细自动汇总:Σ份数×(包装物皮重+托盘重量)"/>
</Border>
<!-- ===== Row 8: 库位 / 卸货人 ===== -->
<Border Grid.Row="8" Grid.Column="0"
Background="{DynamicResource SecondaryRegionBrush}"
BorderThickness="0,0,1,1" BorderBrush="{DynamicResource BorderBrush}">
<TextBlock Text="库位" HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="12" Foreground="{DynamicResource PrimaryTextBrush}"/>
</Border>
<Border Grid.Row="7" Grid.Column="1"
<Border Grid.Row="8" Grid.Column="1"
BorderThickness="0,0,1,1" BorderBrush="{DynamicResource BorderBrush}" Padding="4,0">
<Grid>
<TextBox Text="{Binding Entry.WarehouseLocation, UpdateSourceTrigger=PropertyChanged}"
@@ -490,13 +507,13 @@
</TextBlock>
</Grid>
</Border>
<Border Grid.Row="7" Grid.Column="2"
<Border Grid.Row="8" Grid.Column="2"
Background="{DynamicResource SecondaryRegionBrush}"
BorderThickness="0,0,1,1" BorderBrush="{DynamicResource BorderBrush}">
<TextBlock Text="卸货人" HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="12" Foreground="{DynamicResource PrimaryTextBrush}"/>
</Border>
<Border Grid.Row="7" Grid.Column="3"
<Border Grid.Row="8" Grid.Column="3"
BorderThickness="0,0,0,1" BorderBrush="{DynamicResource BorderBrush}" Padding="4,0">
<Grid>
<TextBox Text="{Binding Entry.UnloadOperator, UpdateSourceTrigger=PropertyChanged}"
@@ -522,14 +539,14 @@
</Grid>
</Border>
<!-- ===== Row 8: 备注(末行,无内部底边线) ===== -->
<Border Grid.Row="8" Grid.Column="0"
<!-- ===== Row 9: 备注(末行,无内部底边线) ===== -->
<Border Grid.Row="9" Grid.Column="0"
Background="{DynamicResource SecondaryRegionBrush}"
BorderThickness="0,0,1,0" BorderBrush="{DynamicResource BorderBrush}">
<TextBlock Text="备注" HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="12" Foreground="{DynamicResource PrimaryTextBrush}"/>
</Border>
<Border Grid.Row="8" Grid.Column="1" Grid.ColumnSpan="3" Padding="4,4">
<Border Grid.Row="9" Grid.Column="1" Grid.ColumnSpan="3" Padding="4,4">
<Grid>
<TextBox Text="{Binding Entry.Remark, UpdateSourceTrigger=PropertyChanged}"
TextWrapping="Wrap" AcceptsReturn="True"
@@ -644,42 +661,54 @@
</StackPanel>
</DockPanel>
<StackPanel>
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Disabled">
<StackPanel MinWidth="820">
<!-- 表头 -->
<Grid Background="{DynamicResource SecondaryRegionBrush}" Height="40">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="130"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="140"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="120"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="份数"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="SemiBold" FontSize="13"
FontWeight="SemiBold" FontSize="12"
Foreground="{DynamicResource PrimaryTextBrush}"/>
<TextBlock Grid.Column="1" Text="每份重量(KG)"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="SemiBold" FontSize="13"
FontWeight="SemiBold" FontSize="12"
Foreground="{DynamicResource PrimaryTextBrush}"/>
<TextBlock Grid.Column="2" Text="每份包数"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="SemiBold" FontSize="13"
FontWeight="SemiBold" FontSize="12"
Foreground="{DynamicResource PrimaryTextBrush}"/>
<!-- 库位列保存时非必填点击「生成原材料卡片」时必填ToolTip 给出说明 -->
<TextBlock Grid.Column="3" Text="库位"
<TextBlock Grid.Column="3" Text="包装物皮重"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="SemiBold" FontSize="13"
FontWeight="SemiBold" FontSize="12"
Foreground="{DynamicResource PrimaryTextBrush}"
ToolTip="自动匹配皮重策略;点击可手动选择"/>
<TextBlock Grid.Column="4" Text="托盘重量"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="SemiBold" FontSize="12"
Foreground="{DynamicResource PrimaryTextBrush}"
ToolTip="由皮重策略自动匹配未匹配时为0"/>
<TextBlock Grid.Column="5" Text="库位"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="SemiBold" FontSize="12"
Foreground="{DynamicResource PrimaryTextBrush}"
ToolTip="生成原材料卡片时为必填项"/>
<TextBlock Grid.Column="4" Text="打印标记"
<TextBlock Grid.Column="6" Text="打印标记"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="SemiBold" FontSize="13"
FontWeight="SemiBold" FontSize="12"
Foreground="{DynamicResource PrimaryTextBrush}"/>
<TextBlock Grid.Column="5" Text="操作"
<TextBlock Grid.Column="7" Text="操作"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="SemiBold" FontSize="13"
FontWeight="SemiBold" FontSize="12"
Foreground="{DynamicResource PrimaryTextBrush}"/>
</Grid>
@@ -736,19 +765,21 @@
</DataTemplate.Resources>
<Grid Height="44">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="130"/>
<ColumnDefinition Width="70"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="90"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="100"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="140"/>
<ColumnDefinition Width="80"/>
<ColumnDefinition Width="120"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<TextBox Text="{Binding Portions, UpdateSourceTrigger=PropertyChanged}"
Style="{StaticResource LockableSplitTextBoxStyle}"/>
</Border>
<Border Grid.Column="1" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<TextBox Text="{Binding PortionWeight, UpdateSourceTrigger=PropertyChanged}"
<TextBox Text="{Binding PortionWeightText, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Style="{StaticResource LockableSplitTextBoxStyle}"/>
</Border>
<Border Grid.Column="2" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
@@ -756,6 +787,59 @@
Style="{StaticResource LockableSplitTextBoxStyle}"/>
</Border>
<Border Grid.Column="3" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<Button Command="{Binding DataContext.OpenTareStrategyPickerCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding}"
ToolTip="{Binding TareStrategyDisplay}"
Cursor="Hand"
VerticalAlignment="Center"
Height="32"
Margin="4,0"
Focusable="False">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="True"/>
<Setter Property="ToolTip" Value="点击选择皮重策略"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasCard}" Value="True">
<Setter Property="IsEnabled" Value="False"/>
<Setter Property="ToolTip" Value="该行已生成原材料卡片,不可修改。如需调整请先点「重新拆码」清空全部卡片"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Button.Template>
<ControlTemplate TargetType="Button">
<Border x:Name="Bd"
BorderBrush="#D9D9D9" BorderThickness="1"
CornerRadius="2"
Background="White">
<TextBlock HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="13"
Text="{Binding PackagingTare, StringFormat={}{0:0.##}}"
Foreground="{DynamicResource PrimaryTextBrush}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="Bd" Property="BorderBrush" Value="#4096FF"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter TargetName="Bd" Property="Background" Value="#F0F7FF"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Bd" Property="Background" Value="#F5F5F5"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</Border>
<Border Grid.Column="4" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<TextBlock Text="{Binding PalletWeight, StringFormat={}{0:0.##}}"
HorizontalAlignment="Center" VerticalAlignment="Center"
FontSize="12" Foreground="{DynamicResource SecondaryTextBrush}"
ToolTip="{Binding TareStrategyDisplay}"/>
</Border>
<Border Grid.Column="5" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<!--
InputBindings 内 RelativeSource 不在可视树中,查找会静默失败。
改用 Button + ControlTemplateCommand 写在 Button 元素上可视树正常RelativeSource 可靠。
@@ -836,11 +920,7 @@
</Button.Template>
</Button>
</Border>
<!--
打印标记列行级状态HasCard与「继续拆码」流程契合。
旧行(已生成卡片)显示绿色「已打印」;新增的待生成行显示灰色「未打印」。
-->
<Border Grid.Column="4" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<Border Grid.Column="6" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<Grid>
<Border CornerRadius="2" Padding="6,2" VerticalAlignment="Center" HorizontalAlignment="Center">
<Border.Style>
@@ -873,7 +953,7 @@
</Border>
</Grid>
</Border>
<Border Grid.Column="5" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<Border Grid.Column="7" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<!-- 行级HasCard==true该行已生成卡片时隐藏删除按钮新增的待生成行可正常删除 -->
<Button Content="删除"
Command="{Binding DataContext.RemoveSplitDetailCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
@@ -897,6 +977,7 @@
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</ScrollViewer>
</StackPanel>
</Border>
</StackPanel>

View File

@@ -0,0 +1,48 @@
<UserControl x:Class="YY.Admin.Views.RawMaterialEntry.TareStrategyPickerDialogView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Width="820" MinHeight="420">
<Grid Background="{DynamicResource ThirdlyRegionBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Margin="20,16,20,8" Text="选择皮重策略"
FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryTextBrush}"/>
<DataGrid Grid.Row="1" Margin="16,0"
ItemsSource="{Binding Records}"
SelectedItem="{Binding SelectedStrategy}"
AutoGenerateColumns="False"
IsReadOnly="True"
CanUserAddRows="False"
HeadersVisibility="Column">
<DataGrid.Columns>
<DataGridTextColumn Header="物料规格" Binding="{Binding MaterialSpec}" Width="100"/>
<DataGridTextColumn Header="包装物重量" Binding="{Binding TareWeight}" Width="100"/>
<DataGridTextColumn Header="托盘重量" Binding="{Binding PalletWeight}" Width="100"/>
<DataGridTextColumn Header="单位" Binding="{Binding UnitName}" Width="80"/>
<DataGridTextColumn Header="生效开始" Binding="{Binding EffectiveStartDate, StringFormat=yyyy-MM-dd}" Width="110"/>
<DataGridTextColumn Header="生效截止" Binding="{Binding EffectiveEndDate, StringFormat=yyyy-MM-dd}" Width="110"/>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="2" Margin="16,12">
<TextBlock Text="{Binding SelectedStrategyDisplay}"
Foreground="{DynamicResource SecondaryTextBrush}"
TextWrapping="Wrap" Margin="0,0,0,10"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<Button Content="取消" Command="{Binding CancelCommand}" Style="{StaticResource ButtonDefault}" Width="100" Margin="0,0,10,0"/>
<Button Content="使用自动匹配" Command="{Binding UseAutoMatchCommand}" Style="{StaticResource ButtonDefault}" Width="120" Margin="0,0,10,0"/>
<Button Content="确认选择" Command="{Binding ConfirmCommand}" Style="{StaticResource ButtonPrimary}" Width="100"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,9 @@
namespace YY.Admin.Views.RawMaterialEntry;
public partial class TareStrategyPickerDialogView
{
public TareStrategyPickerDialogView()
{
InitializeComponent();
}
}

View File

@@ -140,6 +140,7 @@
<DataGridTextColumn Header="供应商(发货单位)" Binding="{Binding SenderUnit}" Width="*"/>
<DataGridTextColumn Header="净重(KG)" Binding="{Binding NetWeight, StringFormat=N2}" Width="90"/>
<DataGridTextColumn Header="已入场(KG)" Binding="{Binding EnteredWeight, StringFormat=N2}" Width="100"/>
<DataGridTextColumn Header="货物皮重" Binding="{Binding CargoTareWeight, StringFormat=N2}" Width="90"/>
<DataGridTextColumn Header="称重日期" Binding="{Binding WeighDate, StringFormat='yyyy-MM-dd'}" Width="110"/>
</DataGrid.Columns>
</DataGrid>

View File

@@ -148,6 +148,8 @@
<DataGridTextColumn Header="皮重(KG)" Binding="{Binding TareWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
<DataGridTextColumn Header="净重(KG)" Binding="{Binding NetWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
<DataGridTextColumn Header="已入场重量(KG)" Binding="{Binding EnteredWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="130"/>
<DataGridTextColumn Header="货物皮重" Binding="{Binding CargoTareWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
<DataGridTextColumn Header="原料重量" Binding="{Binding RawMaterialWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
<DataGridTextColumn Header="司机" Binding="{Binding DriverName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="90"/>
<DataGridTextColumn Header="手机号" Binding="{Binding DriverPhone}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="120"/>
<DataGridTextColumn Header="创建时间" Binding="{Binding CreateTime, StringFormat='yyyy-MM-dd HH:mm'}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="130"/>

View File

@@ -179,12 +179,54 @@ WHERE NOT EXISTS (
grantLoginSettingsRole.ExecuteNonQuery();
}
// 基础资料下「密炼物料皮重策略」菜单
long tareStrategyMenuId = 1300150011101;
var ensureTareStrategy = conn.CreateCommand();
ensureTareStrategy.Transaction = tx;
ensureTareStrategy.CommandText = @"
INSERT INTO sys_menu (id, pid, type, name, path, component, title, icon, is_iframe, is_hide, is_keep_alive, is_affix, order_no, status, create_time)
SELECT $id, $pid, 2, 'mesXslMixerMaterialTareStrategy', '/xslmes/mesXslMixerMaterialTareStrategy', 'MixerMaterialTareStrategyListView', '密炼物料皮重策略', '&#xe7ce;', 0, 0, 1, 0, 110, 1, datetime('now')
WHERE NOT EXISTS (SELECT 1 FROM sys_menu WHERE id = $id OR name = 'mesXslMixerMaterialTareStrategy');
";
ensureTareStrategy.Parameters.AddWithValue("$id", tareStrategyMenuId);
ensureTareStrategy.Parameters.AddWithValue("$pid", baseMenuId);
ensureTareStrategy.ExecuteNonQuery();
var grantTareStrategyTenant = conn.CreateCommand();
grantTareStrategyTenant.Transaction = tx;
grantTareStrategyTenant.CommandText = @"
INSERT INTO sys_tenant_menu (tenant_id, menu_id)
SELECT 1300000000001, $menuId
WHERE NOT EXISTS (
SELECT 1 FROM sys_tenant_menu t WHERE t.tenant_id = 1300000000001 AND t.menu_id = $menuId
);
";
grantTareStrategyTenant.Parameters.AddWithValue("$menuId", tareStrategyMenuId);
grantTareStrategyTenant.ExecuteNonQuery();
if (roleObj != null && roleObj != DBNull.Value)
{
var adminRoleId = Convert.ToInt64(roleObj);
var grantTareStrategyRole = conn.CreateCommand();
grantTareStrategyRole.Transaction = tx;
grantTareStrategyRole.CommandText = @"
INSERT INTO sys_role_menu (id, role_id, menu_id)
SELECT (SELECT IFNULL(MAX(id),0)+1 FROM sys_role_menu), $roleId, $menuId
WHERE NOT EXISTS (
SELECT 1 FROM sys_role_menu r WHERE r.role_id = $roleId AND r.menu_id = $menuId
);
";
grantTareStrategyRole.Parameters.AddWithValue("$roleId", adminRoleId);
grantTareStrategyRole.Parameters.AddWithValue("$menuId", tareStrategyMenuId);
grantTareStrategyRole.ExecuteNonQuery();
}
var verify = conn.CreateCommand();
verify.Transaction = tx;
verify.CommandText = @"
SELECT id, pid, title, path, name, component, type, status, order_no
FROM sys_menu
WHERE title IN ('基础资料','车辆管理','客户管理')
WHERE title IN ('基础资料','车辆管理','客户管理','密炼物料皮重策略')
ORDER BY title, id;
";
using var reader = verify.ExecuteReader();