新增磅单管理功能,支持免密接口和数据同步,更新相关控制器、实体和服务,优化权限管理,确保系统的灵活性和可扩展性。

This commit is contained in:
geht
2026-05-06 15:30:31 +08:00
parent b03cbeff9b
commit 71b8d94da8
48 changed files with 4205 additions and 3 deletions

View File

@@ -134,6 +134,10 @@ public class StompWebSocketService : ISignalRService
await SendFrameAsync(
BuildSubscribeFrame("sub-mes-suppliers", "/topic/sync/mes-suppliers"),
cancellationToken).ConfigureAwait(false);
// 磅单数据变更:订阅 /topic/sync/mes-weight-records
await SendFrameAsync(
BuildSubscribeFrame("sub-mes-weight-records", "/topic/sync/mes-weight-records"),
cancellationToken).ConfigureAwait(false);
// 订阅服务端 PONG 回复(应用层假在线检测)
await SendFrameAsync(

View File

@@ -11,6 +11,7 @@ using YY.Admin.Views.Customer;
using YY.Admin.Views.Supplier;
using YY.Admin.ViewModels.Vehicle;
using YY.Admin.Views.Vehicle;
using YY.Admin.Views.WeightRecord;
namespace YY.Admin
{
@@ -61,6 +62,10 @@ namespace YY.Admin
containerRegistry.RegisterForNavigation<CustomerListView>();
// 供应商管理
containerRegistry.RegisterForNavigation<SupplierListView>();
// 磅单记录管理标准CRUD列表
containerRegistry.RegisterForNavigation<WeightRecordListView>();
// 地磅称重操作(大页面操作台)
containerRegistry.RegisterForNavigation<WeightRecordOperationView>();
}
}
public class DialogWindow : Window, IDialogWindow

View File

@@ -16,6 +16,7 @@ using YY.Admin.Infrastructure.Sync;
using YY.Admin.Services.Service.Customer;
using YY.Admin.Services.Service.Supplier;
using YY.Admin.Services.Service.Vehicle;
using YY.Admin.Services.Service.WeightRecord;
namespace YY.Admin.Module;
@@ -42,6 +43,9 @@ public class SyncModule : IModule
// 供应商管理:免密 API 直连 + STOMP 实时通知
containerRegistry.RegisterSingleton<ISupplierService, SupplierService>();
containerRegistry.RegisterSingleton<SupplierSyncCoordinator>();
// 磅单管理:免密 API 直连 + STOMP 实时通知
containerRegistry.RegisterSingleton<IWeightRecordService, WeightRecordService>();
containerRegistry.RegisterSingleton<WeightRecordSyncCoordinator>();
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<DisconnectGuardHandler>();
@@ -92,6 +96,8 @@ public class SyncModule : IModule
_ = containerProvider.Resolve<CustomerSyncCoordinator>();
// 强制实例化供应商同步协调器
_ = containerProvider.Resolve<SupplierSyncCoordinator>();
// 强制实例化磅单同步协调器
_ = containerProvider.Resolve<WeightRecordSyncCoordinator>();
}
private static IAsyncPolicy<HttpResponseMessage> GetRetryPolicy()

View File

@@ -98,7 +98,17 @@ namespace YY.Admin.ViewModels.Control
["SupplierListView"] = "SupplierListView",
["/xslmes/mesXslSupplier"] = "SupplierListView",
["/xslmes/supplier"] = "SupplierListView",
["mesXslSupplier"] = "SupplierListView"
["mesXslSupplier"] = "SupplierListView",
// 已实现页面:磅单记录管理
["WeightRecordListView"] = "WeightRecordListView",
["/xslmes/mesXslWeightRecord"] = "WeightRecordListView",
["mesXslWeightRecord"] = "WeightRecordListView",
// 已实现页面:地磅称重操作
["WeightRecordOperationView"] = "WeightRecordOperationView",
["/xslmes/weightRecordOperation"] = "WeightRecordOperationView",
["weightRecordOperation"] = "WeightRecordOperationView"
};
private MenuItem? _selectedMenuItem;

View File

@@ -382,8 +382,9 @@ namespace YY.Admin.ViewModels
}
else
{
var exMsg = result.Exception?.Message;
_logger.Error($"导航失败: {viewName}, Region={regionName}, Exception={exMsg}");
var exMsg = result.Exception?.ToString() ?? "null";
var innerExMsg = result.Exception?.InnerException?.ToString() ?? "null";
_logger.Error($"导航失败: {viewName}, Region={regionName}, Exception={exMsg}, InnerException={innerExMsg}");
tcs.SetResult(false);
}
}, parameters);

View File

@@ -0,0 +1,84 @@
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.WeightRecord;
public class CustomerPickerDialogViewModel : BaseViewModel, IDialogResultable<bool>
{
private readonly ICustomerService _customerService;
private string? _searchText;
public string? SearchText { get => _searchText; set => SetProperty(ref _searchText, value); }
public ObservableCollection<MesXslCustomer> Customers { get; } = new();
private MesXslCustomer? _selectedCustomer;
public MesXslCustomer? SelectedCustomer
{
get => _selectedCustomer;
set
{
SetProperty(ref _selectedCustomer, value);
ConfirmCommand.RaiseCanExecuteChanged();
RaisePropertyChanged(nameof(SelectedCustomerDisplay));
RaisePropertyChanged(nameof(HasSelectedCustomer));
}
}
public string SelectedCustomerDisplay => _selectedCustomer != null
? $"[{_selectedCustomer.CustomerCode}] {_selectedCustomer.CustomerName}"
: "选中客户后点击「确认选择」";
public bool HasSelectedCustomer => _selectedCustomer != 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 CustomerPickerDialogViewModel(
ICustomerService customerService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_customerService = customerService;
SearchCommand = new DelegateCommand(async () => await LoadAsync());
ConfirmCommand = new DelegateCommand(Confirm, () => SelectedCustomer != null);
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
_ = LoadAsync();
}
private async Task LoadAsync()
{
try
{
IsLoading = true;
var keyword = SearchText?.Trim();
var result = await _customerService.PageAsync(1, 200, customerName: keyword);
Customers.Clear();
foreach (var c in result.Records)
Customers.Add(c);
}
catch
{
Customers.Clear();
}
finally
{
IsLoading = false;
}
}
private void Confirm()
{
if (SelectedCustomer == null) return;
Result = true;
CloseAction?.Invoke();
}
}

View File

@@ -0,0 +1,84 @@
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.WeightRecord;
public class SupplierPickerDialogViewModel : BaseViewModel, IDialogResultable<bool>
{
private readonly ISupplierService _supplierService;
private string? _searchText;
public string? SearchText { get => _searchText; set => SetProperty(ref _searchText, value); }
public ObservableCollection<MesXslSupplier> Suppliers { get; } = new();
private MesXslSupplier? _selectedSupplier;
public MesXslSupplier? SelectedSupplier
{
get => _selectedSupplier;
set
{
SetProperty(ref _selectedSupplier, value);
ConfirmCommand.RaiseCanExecuteChanged();
RaisePropertyChanged(nameof(SelectedSupplierDisplay));
RaisePropertyChanged(nameof(HasSelectedSupplier));
}
}
public string SelectedSupplierDisplay => _selectedSupplier != null
? $"[{_selectedSupplier.SupplierCode}] {_selectedSupplier.SupplierName}"
: "选中供应商后点击「确认选择」";
public bool HasSelectedSupplier => _selectedSupplier != 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 SupplierPickerDialogViewModel(
ISupplierService supplierService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_supplierService = supplierService;
SearchCommand = new DelegateCommand(async () => await LoadAsync());
ConfirmCommand = new DelegateCommand(Confirm, () => SelectedSupplier != null);
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
_ = LoadAsync();
}
private async Task LoadAsync()
{
try
{
IsLoading = true;
var keyword = SearchText?.Trim();
var result = await _supplierService.PageAsync(1, 200, supplierName: keyword);
Suppliers.Clear();
foreach (var s in result.Records)
Suppliers.Add(s);
}
catch
{
Suppliers.Clear();
}
finally
{
IsLoading = false;
}
}
private void Confirm()
{
if (SelectedSupplier == null) return;
Result = true;
CloseAction?.Invoke();
}
}

View File

@@ -0,0 +1,145 @@
using HandyControl.Controls;
using HandyControl.Tools.Extension;
using System.Collections.ObjectModel;
using YY.Admin.Core;
using YY.Admin.Core.Entity;
using YY.Admin.Core.Services;
using YY.Admin.Services.Service;
namespace YY.Admin.ViewModels.WeightRecord;
public class WeightRecordEditDialogViewModel : BaseViewModel, IDialogResultable<bool>
{
private readonly IWeightRecordService _weightRecordService;
private readonly IJeecgDictSyncService _dictSyncService;
private MesXslWeightRecord? _record;
public MesXslWeightRecord? Record
{
get => _record;
set => SetProperty(ref _record, value);
}
public bool IsAddMode => string.IsNullOrWhiteSpace(Record?.Id);
public string DialogTitle => IsAddMode ? "新增磅单" : "编辑磅单";
public ObservableCollection<KeyValuePair<string, string>> InoutDirectionOptions { get; } = new();
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 WeightRecordEditDialogViewModel(
IWeightRecordService weightRecordService,
IJeecgDictSyncService dictSyncService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_weightRecordService = weightRecordService;
_dictSyncService = dictSyncService;
SaveCommand = new DelegateCommand(async () => await SaveAsync());
CancelCommand = new DelegateCommand(() => CloseAction?.Invoke());
_ = LoadDictOptionsAsync();
}
private async Task LoadDictOptionsAsync()
{
try
{
var options = await _dictSyncService.GetDictOptionsAsync("xslmes_inout_direction");
InoutDirectionOptions.Clear();
foreach (var item in options) InoutDirectionOptions.Add(item);
if (InoutDirectionOptions.Count == 0) AddDefaultDirectionOptions();
}
catch { AddDefaultDirectionOptions(); }
}
private void AddDefaultDirectionOptions()
{
InoutDirectionOptions.Clear();
InoutDirectionOptions.Add(new KeyValuePair<string, string>("进厂", "1"));
InoutDirectionOptions.Add(new KeyValuePair<string, string>("出厂", "2"));
}
public void InitializeForAdd()
{
Record = new MesXslWeightRecord
{
WeighDate = DateTime.Today,
InoutDirection = "1"
};
RaisePropertyChanged(nameof(IsAddMode));
RaisePropertyChanged(nameof(DialogTitle));
}
public void InitializeForEdit(MesXslWeightRecord record)
{
Record = new MesXslWeightRecord
{
Id = record.Id,
BillNo = record.BillNo,
WeighDate = record.WeighDate,
InoutDirection = record.InoutDirection,
VehicleId = record.VehicleId,
PlateNumber = record.PlateNumber,
SenderUnit = record.SenderUnit,
ReceiverUnit = record.ReceiverUnit,
GoodsName = record.GoodsName,
GrossWeight = record.GrossWeight,
TareWeight = record.TareWeight,
NetWeight = record.NetWeight,
DriverName = record.DriverName,
DriverPhone = record.DriverPhone,
BillType = record.BillType,
TenantId = record.TenantId,
UpdateTime = record.UpdateTime
};
RaisePropertyChanged(nameof(IsAddMode));
RaisePropertyChanged(nameof(DialogTitle));
}
private async Task SaveAsync()
{
if (Record == null) return;
if (string.IsNullOrWhiteSpace(Record.PlateNumber))
{
HandyControl.Controls.MessageBox.Warning("车牌号不能为空!");
return;
}
if (string.IsNullOrWhiteSpace(Record.InoutDirection))
{
HandyControl.Controls.MessageBox.Warning("进出方向不能为空!");
return;
}
// 净重自动计算
if (Record.GrossWeight.HasValue && Record.TareWeight.HasValue)
Record.NetWeight = Math.Round(Record.GrossWeight.Value - Record.TareWeight.Value, 2);
try
{
bool ok;
if (IsAddMode)
{
ok = await _weightRecordService.AddAsync(Record);
if (ok) HandyControl.Controls.MessageBox.Success("新增磅单成功!");
else { HandyControl.Controls.MessageBox.Error("新增磅单失败!"); return; }
}
else
{
ok = await _weightRecordService.EditAsync(Record);
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,232 @@
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.Events;
using YY.Admin.Core.Helper;
using YY.Admin.Core.Entity;
using YY.Admin.Core.Services;
using YY.Admin.Services.Service;
using YY.Admin.Views.WeightRecord;
namespace YY.Admin.ViewModels.WeightRecord;
public class WeightRecordListViewModel : BaseViewModel
{
private readonly IWeightRecordService _weightRecordService;
private readonly IJeecgDictSyncService _dictSyncService;
private readonly IDialogService _dialogService;
private SubscriptionToken? _changedToken;
private SubscriptionToken? _conflictToken;
private ObservableCollection<MesXslWeightRecord> _records = new();
public ObservableCollection<MesXslWeightRecord> Records
{
get => _records;
set => SetProperty(ref _records, 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? _filterBillNo;
public string? FilterBillNo { get => _filterBillNo; set => SetProperty(ref _filterBillNo, value); }
private string? _filterPlateNumber;
public string? FilterPlateNumber { get => _filterPlateNumber; set => SetProperty(ref _filterPlateNumber, value); }
private string? _filterInoutDirection;
public string? FilterInoutDirection { get => _filterInoutDirection; set => SetProperty(ref _filterInoutDirection, value); }
private string? _filterGoodsName;
public string? FilterGoodsName { get => _filterGoodsName; set => SetProperty(ref _filterGoodsName, value); }
private string? _filterDriverName;
public string? FilterDriverName { get => _filterDriverName; set => SetProperty(ref _filterDriverName, value); }
public ObservableCollection<KeyValuePair<string, string>> InoutDirectionOptions { get; } = new();
public ObservableCollection<KeyValuePair<string, string>> BillTypeOptions { get; } = new();
public DelegateCommand SearchCommand { get; }
public DelegateCommand ResetCommand { get; }
public DelegateCommand AddCommand { get; }
public DelegateCommand<MesXslWeightRecord> EditCommand { get; }
public DelegateCommand<MesXslWeightRecord> DeleteCommand { get; }
public DelegateCommand PrevPageCommand { get; }
public DelegateCommand NextPageCommand { get; }
public WeightRecordListViewModel(
IWeightRecordService weightRecordService,
IJeecgDictSyncService dictSyncService,
IContainerExtension container,
IDialogService dialogService,
IRegionManager regionManager) : base(container, regionManager)
{
_weightRecordService = weightRecordService;
_dictSyncService = dictSyncService;
_dialogService = dialogService;
SearchCommand = new DelegateCommand(async () => { PageNo = 1; await LoadAsync(); });
ResetCommand = new DelegateCommand(async () =>
{
FilterBillNo = null; FilterPlateNumber = null; FilterInoutDirection = null;
FilterGoodsName = null; FilterDriverName = null;
PageNo = 1;
await LoadAsync();
});
AddCommand = new DelegateCommand(async () => await ShowAddDialogAsync());
EditCommand = new DelegateCommand<MesXslWeightRecord>(async r => await ShowEditDialogAsync(r));
DeleteCommand = new DelegateCommand<MesXslWeightRecord>(async r => await DeleteAsync(r));
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<MesXslWeightRecordChangedEvent>()
.Subscribe(async _ => await LoadAsync(), ThreadOption.UIThread);
_conflictToken = _eventAggregator.GetEvent<SyncConflictEvent>()
.Subscribe(OnSyncConflict, ThreadOption.UIThread);
_ = InitializeAsync();
}
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 LoadDictOptionsAsync();
await UIHelper.WaitForRenderAsync();
await LoadAsync();
}
catch (Exception ex) { Debug.WriteLine($"磅单列表初始化失败: {ex.Message}"); }
}
private async Task LoadDictOptionsAsync()
{
try
{
var options = await _dictSyncService.GetDictOptionsAsync("xslmes_inout_direction", includeAll: true);
InoutDirectionOptions.Clear();
foreach (var item in options) InoutDirectionOptions.Add(item);
if (InoutDirectionOptions.Count == 0) InoutDirectionOptions.Add(new KeyValuePair<string, string>("全部", ""));
var billTypeOptions = await _dictSyncService.GetDictOptionsAsync("xslmes_weight_bill_type", includeAll: true);
BillTypeOptions.Clear();
foreach (var item in billTypeOptions) BillTypeOptions.Add(item);
if (BillTypeOptions.Count == 0) AddDefaultBillTypeOptions();
}
catch
{
InoutDirectionOptions.Clear();
InoutDirectionOptions.Add(new KeyValuePair<string, string>("全部", ""));
InoutDirectionOptions.Add(new KeyValuePair<string, string>("进厂", "1"));
InoutDirectionOptions.Add(new KeyValuePair<string, string>("出厂", "2"));
AddDefaultBillTypeOptions();
}
}
private void AddDefaultBillTypeOptions()
{
BillTypeOptions.Clear();
BillTypeOptions.Add(new KeyValuePair<string, string>("全部", ""));
BillTypeOptions.Add(new KeyValuePair<string, string>("已称毛重", "1"));
BillTypeOptions.Add(new KeyValuePair<string, string>("称重完成", "2"));
BillTypeOptions.Add(new KeyValuePair<string, string>("已称皮重", "3"));
}
public async Task LoadAsync()
{
try
{
IsLoading = true;
var result = await _weightRecordService.PageAsync(
PageNo, PageSize, FilterBillNo, FilterPlateNumber, FilterInoutDirection, FilterGoodsName, FilterDriverName);
// 填充字典显示文本
var dictMap = InoutDirectionOptions.ToDictionary(x => x.Value, x => x.Key);
var billTypeMap = BillTypeOptions.ToDictionary(x => x.Value, x => x.Key);
foreach (var r in result.Records)
{
r.InoutDirectionText = dictMap.TryGetValue(r.InoutDirection ?? "", out var txt) ? txt : r.InoutDirection ?? "";
r.BillTypeText = billTypeMap.TryGetValue(r.BillType ?? "", out var billTypeTxt) ? billTypeTxt : r.BillType ?? "";
}
Records = new ObservableCollection<MesXslWeightRecord>(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<WeightRecordEditDialogView>()
.Initialize<WeightRecordEditDialogViewModel>(vm => vm.InitializeForAdd())
.GetResultAsync<bool>();
if (result) await LoadAsync();
}
catch (Exception ex) { Growl.Error($"打开新增对话框失败:{ex.Message}"); }
}
private async Task ShowEditDialogAsync(MesXslWeightRecord record)
{
if (record == null) return;
try
{
var result = await HandyControl.Controls.Dialog.Show<WeightRecordEditDialogView>()
.Initialize<WeightRecordEditDialogViewModel>(vm => vm.InitializeForEdit(record))
.GetResultAsync<bool>();
if (result) await LoadAsync();
}
catch (Exception ex) { Growl.Error($"打开编辑对话框失败:{ex.Message}"); }
}
private async Task DeleteAsync(MesXslWeightRecord record)
{
if (record?.Id == null) return;
var confirm = System.Windows.MessageBox.Show(
$"确定删除磅单 {record.BillNo ?? record.PlateNumber}?此操作不可恢复!",
"确认删除", MessageBoxButton.OKCancel, MessageBoxImage.Question);
if (confirm != System.Windows.MessageBoxResult.OK) return;
var ok = await _weightRecordService.DeleteAsync(record.Id);
if (ok) { Growl.Success("删除成功!"); await LoadAsync(); }
else Growl.Error("删除失败!");
}
protected override void CleanUp()
{
base.CleanUp();
if (_changedToken != null)
{
_eventAggregator.GetEvent<MesXslWeightRecordChangedEvent>().Unsubscribe(_changedToken);
_changedToken = null;
}
if (_conflictToken != null)
{
_eventAggregator.GetEvent<SyncConflictEvent>().Unsubscribe(_conflictToken);
_conflictToken = null;
}
}
}

View File

@@ -0,0 +1,877 @@
using HandyControl.Controls;
using HandyControl.Tools.Extension;
using System.Collections.ObjectModel;
using System.Windows.Threading;
using YY.Admin.Core;
using YY.Admin.Core.Entity;
using YY.Admin.Core.Services;
using YY.Admin.Services.Service;
using YY.Admin.Views.WeightRecord;
namespace YY.Admin.ViewModels.WeightRecord;
/// <summary>
/// 地磅称重操作页面 ViewModel
/// 左侧:串口模拟实时数据(重量 + 车牌);右侧:称重信息录入表单。
/// </summary>
public class WeightRecordOperationViewModel : BaseViewModel
{
private readonly IWeightRecordService _weightRecordService;
private readonly IJeecgDictSyncService _dictSyncService;
private readonly IVehicleService _vehicleService;
// ─── 车辆档案匹配 ───
private MesXslVehicle? _matchedVehicle;
private string? _lastLookedUpPlate;
private string _vehicleLookupStatus = "None"; // None | Searching | Matched | NotFound
public string VehicleLookupStatus
{
get => _vehicleLookupStatus;
set
{
SetProperty(ref _vehicleLookupStatus, value);
RaisePropertyChanged(nameof(VehicleMatchText));
RaisePropertyChanged(nameof(ShowVehicleMatchHint));
}
}
public bool ShowVehicleMatchHint => _vehicleLookupStatus != "None";
public string VehicleMatchText => _vehicleLookupStatus switch
{
"Searching" => "正在匹配车辆档案...",
"Matched" => $"✓ 已匹配车辆档案({_matchedVehicle?.VehicleBelongText}",
"NotFound" => "未在档案库中找到,保存后将自动录入",
_ => string.Empty
};
// ─── 串口模拟定时器 ───
private readonly DispatcherTimer _serialTimer;
private readonly Random _rnd = new();
private double _baseWeight = 35000.0; // 基准重量(模拟车辆在磅上)
private int _stableCountdown = 0; // 稳定倒计时(连续稳定帧数)
private bool _vehiclePresent = false; // 是否有车辆在磅上
private int _vehicleDetectCooldown = 0; // 车辆检测冷却帧
private bool _isApplyingSelectedRecord;
// ─── 实时数据属性 ───
private double _currentWeight;
public double CurrentWeight
{
get => _currentWeight;
set
{
SetProperty(ref _currentWeight, value);
RaisePropertyChanged(nameof(CurrentWeightDisplay));
}
}
public string CurrentWeightDisplay => $"{CurrentWeight:N2}";
private bool _isWeightStable;
public bool IsWeightStable { get => _isWeightStable; set => SetProperty(ref _isWeightStable, value); }
private string _serialStatusText = "正在连接...";
public string SerialStatusText { get => _serialStatusText; set => SetProperty(ref _serialStatusText, value); }
private bool _isSerialConnected;
public bool IsSerialConnected { get => _isSerialConnected; set => SetProperty(ref _isSerialConnected, value); }
private string _detectedPlate = string.Empty;
public string DetectedPlate { get => _detectedPlate; set => SetProperty(ref _detectedPlate, value); }
private bool _hasPlate;
public bool HasPlate { get => _hasPlate; set => SetProperty(ref _hasPlate, value); }
public ObservableCollection<string> OperationLogs { get; } = new();
// ─── 采集状态 ───
private bool _grossWeightCaptured;
public bool GrossWeightCaptured { get => _grossWeightCaptured; set => SetProperty(ref _grossWeightCaptured, value); }
private bool _tareWeightCaptured;
public bool TareWeightCaptured { get => _tareWeightCaptured; set => SetProperty(ref _tareWeightCaptured, value); }
// ─── 表单绑定属性 ───
private DateTime _weighDate = DateTime.Today;
public DateTime WeighDate { get => _weighDate; set => SetProperty(ref _weighDate, value); }
private string? _inoutDirection = "1";
public string? InoutDirection
{
get => _inoutDirection;
set
{
if (!SetProperty(ref _inoutDirection, value)) return;
RaisePropertyChanged(nameof(CaptureGrossButtonText));
RaisePropertyChanged(nameof(CaptureTareButtonText));
CaptureGrossWeightCommand.RaiseCanExecuteChanged();
CaptureTareWeightCommand.RaiseCanExecuteChanged();
if (_isApplyingSelectedRecord) return;
if (SelectedRecentWeightRecord != null)
{
ClearFormByDirectionSwitch();
}
_ = LoadRecentWeightRecordsAsync(PlateNumber);
}
}
public string CaptureGrossButtonText => "采集毛重";
public string CaptureTareButtonText => "采集皮重";
private string? _plateNumber;
public string? PlateNumber
{
get => _plateNumber;
set
{
if (!SetProperty(ref _plateNumber, value)) return;
if (_isApplyingSelectedRecord) return;
_ = LoadRecentWeightRecordsAsync(value);
var trimmed = value?.Trim() ?? string.Empty;
if (trimmed.Length >= 6)
_ = LookupVehicleByPlateAsync(trimmed);
else
ClearVehicleMatch();
}
}
// ─── 发货/收货单位(含选择器状态) ───
private MesXslSupplier? _selectedSupplier;
private MesXslCustomer? _selectedCustomer;
public bool HasSelectedSupplier => _selectedSupplier != null;
public bool HasSelectedCustomer => _selectedCustomer != null;
public string SenderUnitDisplay => _selectedSupplier != null
? $"[{_selectedSupplier.SupplierCode}] {_selectedSupplier.SupplierName}"
: (!string.IsNullOrWhiteSpace(_senderUnit) ? _senderUnit : "点击右侧「选择」从供应商列表中选取");
public string ReceiverUnitDisplay => _selectedCustomer != null
? $"[{_selectedCustomer.CustomerCode}] {_selectedCustomer.CustomerName}"
: (!string.IsNullOrWhiteSpace(_receiverUnit) ? _receiverUnit : "点击右侧「选择」从客户列表中选取");
private string? _senderUnit;
public string? SenderUnit
{
get => _senderUnit;
set
{
SetProperty(ref _senderUnit, value);
RaisePropertyChanged(nameof(SenderUnitDisplay));
}
}
private string? _receiverUnit;
public string? ReceiverUnit
{
get => _receiverUnit;
set
{
SetProperty(ref _receiverUnit, value);
RaisePropertyChanged(nameof(ReceiverUnitDisplay));
}
}
private string? _goodsName;
public string? GoodsName { get => _goodsName; set => SetProperty(ref _goodsName, value); }
private double? _grossWeight;
public double? GrossWeight
{
get => _grossWeight;
set { SetProperty(ref _grossWeight, value); RecalcNetWeight(); }
}
private double? _tareWeight;
public double? TareWeight
{
get => _tareWeight;
set { SetProperty(ref _tareWeight, value); RecalcNetWeight(); }
}
private double? _netWeight;
public double? NetWeight { get => _netWeight; set => SetProperty(ref _netWeight, value); }
public string NetWeightDisplay => NetWeight.HasValue ? $"{NetWeight.Value:N2}" : "—";
private string? _driverName;
public string? DriverName { get => _driverName; set => SetProperty(ref _driverName, value); }
private string? _driverPhone;
public string? DriverPhone { get => _driverPhone; set => SetProperty(ref _driverPhone, value); }
// ─── 字典 ───
public ObservableCollection<KeyValuePair<string, string>> InoutDirectionOptions { get; } = new();
public ObservableCollection<WeightRecordSimpleItem> RecentWeightRecords { get; } = new();
private bool _isPlateNumberLocked;
public bool IsPlateNumberLocked
{
get => _isPlateNumberLocked;
set => SetProperty(ref _isPlateNumberLocked, value);
}
private WeightRecordSimpleItem? _selectedRecentWeightRecord;
public WeightRecordSimpleItem? SelectedRecentWeightRecord
{
get => _selectedRecentWeightRecord;
set
{
if (!SetProperty(ref _selectedRecentWeightRecord, value)) return;
IsPlateNumberLocked = value?.Source?.Id != null;
if (value == null) return;
ApplySelectedRecordToForm(value);
}
}
// ─── 命令 ───
public DelegateCommand CaptureGrossWeightCommand { get; }
public DelegateCommand CaptureTareWeightCommand { get; }
public DelegateCommand SaveCommand { get; }
public DelegateCommand ClearCommand { get; }
public DelegateCommand UseDetectedPlateCommand { get; }
public DelegateCommand OpenSupplierPickerCommand { get; }
public DelegateCommand OpenCustomerPickerCommand { get; }
public DelegateCommand ClearSupplierCommand { get; }
public DelegateCommand ClearCustomerCommand { get; }
public WeightRecordOperationViewModel(
IWeightRecordService weightRecordService,
IJeecgDictSyncService dictSyncService,
IVehicleService vehicleService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_weightRecordService = weightRecordService;
_dictSyncService = dictSyncService;
_vehicleService = vehicleService;
CaptureGrossWeightCommand = new DelegateCommand(CaptureGrossWeight, CanCaptureGrossWeight)
.ObservesProperty(() => IsWeightStable)
.ObservesProperty(() => GrossWeightCaptured)
.ObservesProperty(() => TareWeightCaptured);
CaptureTareWeightCommand = new DelegateCommand(CaptureTareWeight, CanCaptureTareWeight)
.ObservesProperty(() => IsWeightStable)
.ObservesProperty(() => GrossWeightCaptured)
.ObservesProperty(() => TareWeightCaptured);
SaveCommand = new DelegateCommand(async () => await SaveAsync());
ClearCommand = new DelegateCommand(ClearForm);
UseDetectedPlateCommand = new DelegateCommand(() => PlateNumber = DetectedPlate, () => HasPlate)
.ObservesProperty(() => HasPlate);
OpenSupplierPickerCommand = new DelegateCommand(async () => await OpenSupplierPickerAsync());
OpenCustomerPickerCommand = new DelegateCommand(async () => await OpenCustomerPickerAsync());
ClearSupplierCommand = new DelegateCommand(ClearSupplierSelection);
ClearCustomerCommand = new DelegateCommand(ClearCustomerSelection);
_ = LoadDictOptionsAsync();
_ = LoadRecentWeightRecordsAsync(PlateNumber);
// 启动串口模拟定时器200ms 刷新一次,约 5Hz
_serialTimer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };
_serialTimer.Tick += OnSerialTimerTick;
_serialTimer.Start();
// 模拟1秒后串口连接成功
var connectTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
connectTimer.Tick += (s, e) =>
{
((DispatcherTimer)s!).Stop();
IsSerialConnected = true;
SerialStatusText = "COM3 9600bps 已连接";
AddLog("串口连接成功 COM3");
// 3秒后模拟第一辆车到来
var vehicleTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(3) };
vehicleTimer.Tick += (s2, e2) => { ((DispatcherTimer)s2!).Stop(); SimulateVehicleArrival(); };
vehicleTimer.Start();
};
connectTimer.Start();
}
private void OnSerialTimerTick(object? sender, EventArgs e)
{
if (!IsSerialConnected) return;
if (_vehiclePresent)
{
// 车辆在磅上:在基准重量附近波动
var noise = (_rnd.NextDouble() - 0.5) * 80;
CurrentWeight = Math.Max(0, _baseWeight + noise);
// 稳定检测连续10帧波动小于30kg视为稳定
if (Math.Abs(noise) < 30) _stableCountdown = Math.Min(_stableCountdown + 1, 10);
else _stableCountdown = Math.Max(_stableCountdown - 2, 0);
IsWeightStable = _stableCountdown >= 8;
}
else
{
// 无车辆:重量归零并稳定
CurrentWeight = Math.Max(0, CurrentWeight - 500);
if (CurrentWeight < 50) { CurrentWeight = 0; IsWeightStable = true; }
else IsWeightStable = false;
}
// 车辆检测冷却计数
if (_vehicleDetectCooldown > 0) _vehicleDetectCooldown--;
}
private void SimulateVehicleArrival()
{
_vehiclePresent = true;
_stableCountdown = 0;
_baseWeight = _rnd.Next(18000, 65000); // 18~65吨随机
AddLog("检测到车辆入场");
// 模拟摄像头识别车牌2秒后回传
var plateTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
plateTimer.Tick += (s, e) =>
{
((DispatcherTimer)s!).Stop();
var plates = new[] { "粤A88888", "粤B12345", "粤C98765", "湘A66666", "川B55555" };
DetectedPlate = plates[_rnd.Next(plates.Length)];
HasPlate = true;
AddLog($"车牌识别:{DetectedPlate}");
};
plateTimer.Start();
// 模拟15秒后车辆离开可手动操作
var leaveTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(30) };
leaveTimer.Tick += (s, e) =>
{
((DispatcherTimer)s!).Stop();
if (_vehiclePresent) SimulateVehicleLeave();
};
leaveTimer.Start();
}
private void SimulateVehicleLeave()
{
_vehiclePresent = false;
_stableCountdown = 0;
HasPlate = false;
AddLog("车辆离场");
// 8秒后下一辆车
if (_vehicleDetectCooldown <= 0)
{
_vehicleDetectCooldown = 40; // 8秒 / 200ms = 40帧
var nextTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(8) };
nextTimer.Tick += (s, e) => { ((DispatcherTimer)s!).Stop(); SimulateVehicleArrival(); };
nextTimer.Start();
}
}
private void CaptureGrossWeight()
{
var weight = Math.Round(CurrentWeight, 2);
GrossWeight = weight;
GrossWeightCaptured = true;
AddLog($"采集毛重:{GrossWeight:N2} kg");
}
private void CaptureTareWeight()
{
var weight = Math.Round(CurrentWeight, 2);
TareWeight = weight;
TareWeightCaptured = true;
AddLog($"采集皮重:{TareWeight:N2} kg");
}
private bool CanCaptureGrossWeight()
{
return IsWeightStable && !GrossWeightCaptured;
}
private bool CanCaptureTareWeight()
{
return IsWeightStable && !TareWeightCaptured;
}
private void RecalcNetWeight()
{
if (GrossWeight.HasValue && TareWeight.HasValue)
{
NetWeight = Math.Round(GrossWeight.Value - TareWeight.Value, 2);
RaisePropertyChanged(nameof(NetWeightDisplay));
}
else
{
NetWeight = null;
RaisePropertyChanged(nameof(NetWeightDisplay));
}
}
private async Task SaveAsync()
{
if (string.IsNullOrWhiteSpace(PlateNumber))
{
HandyControl.Controls.MessageBox.Warning("车牌号不能为空!");
return;
}
var selectedSource = SelectedRecentWeightRecord?.Source;
var isCompleteSelectedRecord = !string.IsNullOrWhiteSpace(selectedSource?.Id);
var isOutbound = string.Equals(InoutDirection, "2", StringComparison.Ordinal);
if (!isCompleteSelectedRecord)
{
if (isOutbound && !TareWeight.HasValue)
{
HandyControl.Controls.MessageBox.Warning("出厂流程请先采集皮重!");
return;
}
if (!isOutbound && !GrossWeight.HasValue)
{
HandyControl.Controls.MessageBox.Warning("进厂流程请先采集毛重!");
return;
}
}
else
{
var needSecondWeightCaptured = isOutbound ? GrossWeight.HasValue : TareWeight.HasValue;
if (!needSecondWeightCaptured)
{
HandyControl.Controls.MessageBox.Warning(isOutbound
? "当前为已称皮重单据补毛重,必须先采集毛重!"
: "当前为已称毛重单据补皮重,必须先采集皮重!");
return;
}
}
var entity = new MesXslWeightRecord
{
Id = selectedSource?.Id,
BillNo = selectedSource?.BillNo,
WeighDate = WeighDate,
InoutDirection = InoutDirection,
PlateNumber = PlateNumber,
SenderUnit = SenderUnit,
ReceiverUnit = ReceiverUnit,
GoodsName = GoodsName,
GrossWeight = GrossWeight,
TareWeight = TareWeight,
NetWeight = NetWeight,
DriverName = DriverName,
DriverPhone = DriverPhone
};
try
{
var ok = isCompleteSelectedRecord
? await _weightRecordService.EditAsync(entity)
: await _weightRecordService.AddAsync(entity);
if (ok)
{
if (isCompleteSelectedRecord)
{
Growl.Success("皮重回填成功,单据已更新为称重完成!");
AddLog(isOutbound
? $"更新成功:{entity.BillNo ?? PlateNumber},毛重 {GrossWeight:N2} kg"
: $"更新成功:{entity.BillNo ?? PlateNumber},皮重 {TareWeight:N2} kg");
}
else
{
Growl.Success("磅单保存成功!");
AddLog($"保存成功:{PlateNumber},净重 {NetWeight:N2} kg");
}
_ = UpsertVehicleAsync();
await LoadRecentWeightRecordsAsync(PlateNumber);
// 保存后重置重量相关字段,保留日期/方向
GrossWeight = null; TareWeight = null; NetWeight = null;
GrossWeightCaptured = false; TareWeightCaptured = false;
SelectedRecentWeightRecord = null;
IsPlateNumberLocked = false;
PlateNumber = null; DetectedPlate = string.Empty; HasPlate = false;
SenderUnit = null; ReceiverUnit = null; GoodsName = null;
DriverName = null; DriverPhone = null;
ClearSupplierSelection();
ClearCustomerSelection();
ClearVehicleMatch();
RaisePropertyChanged(nameof(NetWeightDisplay));
}
else
{
Growl.Error("磅单保存失败!");
}
}
catch (Exception ex)
{
Growl.Error($"保存失败:{ex.Message}");
}
}
private void ClearForm()
{
var confirm = System.Windows.MessageBox.Show(
"确认清空所有填写内容?", "确认清空",
System.Windows.MessageBoxButton.OKCancel,
System.Windows.MessageBoxImage.Question);
if (confirm != System.Windows.MessageBoxResult.OK) return;
WeighDate = DateTime.Today;
InoutDirection = "1";
PlateNumber = null; SenderUnit = null; ReceiverUnit = null;
GoodsName = null; GrossWeight = null; TareWeight = null; NetWeight = null;
DriverName = null; DriverPhone = null;
GrossWeightCaptured = false; TareWeightCaptured = false;
DetectedPlate = string.Empty; HasPlate = false;
SelectedRecentWeightRecord = null;
IsPlateNumberLocked = false;
ClearSupplierSelection();
ClearCustomerSelection();
ClearVehicleMatch();
RaisePropertyChanged(nameof(NetWeightDisplay));
AddLog("表单已清空");
}
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;
var supplier = pickerVm.SelectedSupplier;
_selectedSupplier = supplier;
SenderUnit = supplier.SupplierShortName ?? supplier.SupplierName;
RaisePropertyChanged(nameof(SenderUnitDisplay));
RaisePropertyChanged(nameof(HasSelectedSupplier));
AddLog($"已选发货单位:{supplier.SupplierName}");
}
private async Task OpenCustomerPickerAsync()
{
CustomerPickerDialogViewModel? pickerVm = null;
bool confirmed;
try
{
confirmed = await HandyControl.Controls.Dialog.Show<CustomerPickerDialogView>()
.Initialize<CustomerPickerDialogViewModel>(vm => { pickerVm = vm; })
.GetResultAsync<bool>();
}
catch { return; }
if (!confirmed || pickerVm?.SelectedCustomer == null) return;
var customer = pickerVm.SelectedCustomer;
_selectedCustomer = customer;
ReceiverUnit = customer.CustomerShortName ?? customer.CustomerName;
RaisePropertyChanged(nameof(ReceiverUnitDisplay));
RaisePropertyChanged(nameof(HasSelectedCustomer));
AddLog($"已选收货单位:{customer.CustomerName}");
}
private void ClearSupplierSelection()
{
_selectedSupplier = null;
SenderUnit = null;
RaisePropertyChanged(nameof(SenderUnitDisplay));
RaisePropertyChanged(nameof(HasSelectedSupplier));
}
private void ClearCustomerSelection()
{
_selectedCustomer = null;
ReceiverUnit = null;
RaisePropertyChanged(nameof(ReceiverUnitDisplay));
RaisePropertyChanged(nameof(HasSelectedCustomer));
}
private void AddLog(string message)
{
var entry = $"{DateTime.Now:HH:mm:ss} {message}";
OperationLogs.Insert(0, entry);
while (OperationLogs.Count > 8) OperationLogs.RemoveAt(OperationLogs.Count - 1);
}
private async Task LoadDictOptionsAsync()
{
try
{
var options = await _dictSyncService.GetDictOptionsAsync("xslmes_inout_direction");
InoutDirectionOptions.Clear();
foreach (var item in options) InoutDirectionOptions.Add(item);
if (InoutDirectionOptions.Count == 0) AddDefaultDirectionOptions();
}
catch { AddDefaultDirectionOptions(); }
}
private void AddDefaultDirectionOptions()
{
InoutDirectionOptions.Clear();
InoutDirectionOptions.Add(new KeyValuePair<string, string>("进厂", "1"));
InoutDirectionOptions.Add(new KeyValuePair<string, string>("出厂", "2"));
}
private async Task LoadRecentWeightRecordsAsync(string? plateNumber)
{
try
{
var plate = (plateNumber ?? string.Empty).Trim();
RecentWeightRecords.Clear();
if (string.IsNullOrWhiteSpace(plate))
{
SelectedRecentWeightRecord = null;
return;
}
// 按进出方向查询当天待补称单据:进厂=已称毛重,出厂=已称皮重。
var page = await _weightRecordService.PageAsync(1, 100, filterPlateNumber: plate);
var today = DateTime.Today;
var isOutbound = string.Equals(InoutDirection, "2", StringComparison.Ordinal);
var candidates = page.Records
.Where(r =>
string.Equals((r.PlateNumber ?? string.Empty).Trim(), plate, StringComparison.OrdinalIgnoreCase) &&
r.WeighDate.HasValue &&
r.WeighDate.Value.Date == today &&
(isOutbound
? (r.TareWeight.HasValue && !r.GrossWeight.HasValue)
: (r.GrossWeight.HasValue && !r.TareWeight.HasValue)))
.OrderByDescending(r => r.CreateTime ?? DateTime.MinValue)
.ToList();
foreach (var record in candidates)
{
RecentWeightRecords.Add(new WeightRecordSimpleItem
{
Source = record,
BillNo = record.BillNo ?? "-",
PlateNumber = record.PlateNumber ?? "-",
FirstWeightDisplay = isOutbound
? (record.TareWeight.HasValue ? $"{record.TareWeight.Value:N2}" : "-")
: (record.GrossWeight.HasValue ? $"{record.GrossWeight.Value:N2}" : "-")
});
}
SelectedRecentWeightRecord = null;
}
catch
{
// 查询失败时保持现有列表,不中断主流程
}
}
private void ApplySelectedRecordToForm(WeightRecordSimpleItem selected)
{
if (selected.Source == null) return;
// 带入历史磅单时,不触发车辆重新查询
_matchedVehicle = null;
_lastLookedUpPlate = selected.Source.PlateNumber?.Trim();
VehicleLookupStatus = "None";
_isApplyingSelectedRecord = true;
try
{
// 自动带入基础信息,并按进出方向保留"待补称"的第二次称重位。
WeighDate = selected.Source.WeighDate ?? DateTime.Today;
InoutDirection = selected.Source.InoutDirection;
PlateNumber = selected.Source.PlateNumber;
_selectedSupplier = null;
_selectedCustomer = null;
SenderUnit = selected.Source.SenderUnit;
ReceiverUnit = selected.Source.ReceiverUnit;
RaisePropertyChanged(nameof(SenderUnitDisplay));
RaisePropertyChanged(nameof(ReceiverUnitDisplay));
RaisePropertyChanged(nameof(HasSelectedSupplier));
RaisePropertyChanged(nameof(HasSelectedCustomer));
GoodsName = selected.Source.GoodsName;
DriverName = selected.Source.DriverName;
DriverPhone = selected.Source.DriverPhone;
var isOutbound = string.Equals(InoutDirection, "2", StringComparison.Ordinal);
if (isOutbound)
{
TareWeight = selected.Source.TareWeight;
TareWeightCaptured = TareWeight.HasValue;
GrossWeight = null;
GrossWeightCaptured = false;
}
else
{
GrossWeight = selected.Source.GrossWeight;
GrossWeightCaptured = GrossWeight.HasValue;
TareWeight = null;
TareWeightCaptured = false;
}
AddLog($"已带入榜单:{selected.BillNo}");
}
finally
{
_isApplyingSelectedRecord = false;
}
}
private void ClearFormByDirectionSwitch()
{
// 已选榜单后切换进出方向,清空数据防止串单。
WeighDate = DateTime.Today;
PlateNumber = null;
SenderUnit = null;
ReceiverUnit = null;
GoodsName = null;
DriverName = null;
DriverPhone = null;
GrossWeight = null;
TareWeight = null;
NetWeight = null;
GrossWeightCaptured = false;
TareWeightCaptured = false;
SelectedRecentWeightRecord = null;
IsPlateNumberLocked = false;
ClearSupplierSelection();
ClearCustomerSelection();
ClearVehicleMatch();
RaisePropertyChanged(nameof(NetWeightDisplay));
AddLog("已切换进出方向,当前表单数据已清空");
}
// ─── 车辆档案查询与回写 ───
private async Task LookupVehicleByPlateAsync(string plate)
{
if (string.Equals(plate, _lastLookedUpPlate, StringComparison.OrdinalIgnoreCase)) return;
_lastLookedUpPlate = plate;
VehicleLookupStatus = "Searching";
try
{
var result = await _vehicleService.PageAsync(1, 1, plateNumber: plate);
var vehicle = result.Records.FirstOrDefault(v =>
string.Equals(v.PlateNumber?.Trim(), plate, StringComparison.OrdinalIgnoreCase)
&& v.Status != "1");
if (vehicle != null)
{
_matchedVehicle = vehicle;
VehicleLookupStatus = "Matched";
ApplyVehicleToForm(vehicle);
}
else
{
_matchedVehicle = null;
VehicleLookupStatus = "NotFound";
}
}
catch
{
_matchedVehicle = null;
VehicleLookupStatus = "None";
}
}
private void ApplyVehicleToForm(MesXslVehicle vehicle)
{
// 司机信息优先带出
if (!string.IsNullOrWhiteSpace(vehicle.DriverName)) DriverName = vehicle.DriverName;
if (!string.IsNullOrWhiteSpace(vehicle.DriverPhone)) DriverPhone = vehicle.DriverPhone;
// 根据车辆归属自动判断进出方向与发收货单位
if (vehicle.VehicleBelong == "2") // 供应商 → 进厂
{
if (!_isApplyingSelectedRecord) InoutDirection = "1";
_selectedSupplier = null;
SenderUnit = vehicle.SupplierShortName ?? vehicle.SupplierName;
RaisePropertyChanged(nameof(SenderUnitDisplay));
RaisePropertyChanged(nameof(HasSelectedSupplier));
}
else if (vehicle.VehicleBelong == "1") // 客户 → 出厂
{
if (!_isApplyingSelectedRecord) InoutDirection = "2";
_selectedCustomer = null;
ReceiverUnit = vehicle.CustomerShortName;
RaisePropertyChanged(nameof(ReceiverUnitDisplay));
RaisePropertyChanged(nameof(HasSelectedCustomer));
}
// VehicleBelong == "3"(本公司):方向不定,不自动切换
AddLog($"车辆档案匹配:{vehicle.PlateNumber}{vehicle.VehicleBelongText}");
}
private void ClearVehicleMatch()
{
_matchedVehicle = null;
_lastLookedUpPlate = null;
VehicleLookupStatus = "None";
}
private async Task UpsertVehicleAsync()
{
if (string.IsNullOrWhiteSpace(PlateNumber)) return;
try
{
if (_matchedVehicle != null)
{
// 已有档案:仅在司机信息有变动时回写
bool driverChanged =
!string.Equals(DriverName, _matchedVehicle.DriverName) ||
!string.Equals(DriverPhone, _matchedVehicle.DriverPhone);
if (!driverChanged) return;
await _vehicleService.EditAsync(new MesXslVehicle
{
Id = _matchedVehicle.Id,
PlateNumber = _matchedVehicle.PlateNumber,
VehicleBelong = _matchedVehicle.VehicleBelong,
TareWeightKg = _matchedVehicle.TareWeightKg,
LoadCapacity = _matchedVehicle.LoadCapacity,
SupplierId = _matchedVehicle.SupplierId,
SupplierName = _matchedVehicle.SupplierName,
SupplierShortName = _matchedVehicle.SupplierShortName,
CustomerIds = _matchedVehicle.CustomerIds,
CustomerShortName = _matchedVehicle.CustomerShortName,
DriverName = DriverName,
DriverPhone = DriverPhone,
Status = _matchedVehicle.Status,
TenantId = _matchedVehicle.TenantId,
Version = _matchedVehicle.Version,
});
AddLog($"车辆档案已更新:{PlateNumber}");
}
else
{
// 无档案:根据表单信息新建车辆档案
var vehicleBelong = InoutDirection == "2" ? "1" : "2"; // 出厂→客户 进厂→供应商
var newVehicle = new MesXslVehicle
{
PlateNumber = PlateNumber?.Trim(),
VehicleBelong = vehicleBelong,
SupplierId = _selectedSupplier?.Id,
SupplierName = _selectedSupplier?.SupplierName,
SupplierShortName = _selectedSupplier?.SupplierShortName,
CustomerShortName = _selectedCustomer?.CustomerShortName
?? (InoutDirection == "2" ? ReceiverUnit : null),
DriverName = DriverName,
DriverPhone = DriverPhone,
Status = "0",
};
await _vehicleService.AddAsync(newVehicle);
AddLog($"已新建车辆档案:{PlateNumber}");
}
}
catch
{
// 车辆回写失败不影响磅单保存,静默处理
}
}
protected override void CleanUp()
{
base.CleanUp();
_serialTimer.Stop();
}
}
public class WeightRecordSimpleItem
{
public MesXslWeightRecord? Source { get; set; }
public string BillNo { get; set; } = "-";
public string PlateNumber { get; set; } = "-";
public string FirstWeightDisplay { get; set; } = "-";
}

View File

@@ -0,0 +1,170 @@
<UserControl x:Class="YY.Admin.Views.WeightRecord.CustomerPickerDialogView"
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"
Width="640">
<Grid Background="{DynamicResource ThirdlyRegionBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="320"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 标题栏 -->
<hc:SimplePanel Margin="20,16,20,12">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="32" Height="32" CornerRadius="6" Background="#52c41a" Margin="0,0,10,0">
<md:PackIcon Kind="OfficeBuildingMarker" Width="18" Height="18" Foreground="White"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<TextBlock Text="选择客户(收货单位)" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryTextBrush}" VerticalAlignment="Center"/>
</StackPanel>
<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="Center"/>
</hc:SimplePanel>
<!-- 搜索栏 -->
<Border Grid.Row="1" Background="{DynamicResource RegionBrush}" Padding="16,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<hc:TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Placeholder="输入客户名称搜索..."
hc:InfoElement.ShowClearButton="True"
Margin="0,0,8,0">
<hc:TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
</hc:TextBox.InputBindings>
</hc:TextBox>
<Button Grid.Column="1" Command="{Binding SearchCommand}"
IsEnabled="{Binding IsLoading, Converter={StaticResource Boolean2BooleanReConverter}}"
Style="{StaticResource ButtonPrimary}" Height="32">
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="Magnify" Width="14" Height="14" VerticalAlignment="Center" Margin="0,0,4,0"/>
<TextBlock Text="搜索" FontSize="13" VerticalAlignment="Center"/>
</StackPanel>
</Button>
</Grid>
</Border>
<!-- 客户列表 -->
<DataGrid x:Name="CustomersGrid"
Grid.Row="2"
Margin="16,8,16,0"
ItemsSource="{Binding Customers}"
SelectedItem="{Binding SelectedCustomer}"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserResizeRows="False"
HeadersVisibility="Column"
SelectionMode="Single"
SelectionUnit="FullRow"
IsReadOnly="True"
GridLinesVisibility="Horizontal"
HorizontalGridLinesBrush="#FFEDEFF2"
VerticalGridLinesBrush="Transparent"
Background="White"
RowBackground="White"
AlternatingRowBackground="#FAFCFF"
RowHeight="32"
ColumnHeaderHeight="34"
RowHeaderWidth="0"
MouseDoubleClick="CustomersGrid_MouseDoubleClick">
<!-- 覆盖系统高亮刷,防止失焦后选中行变白 -->
<DataGrid.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#EAF3FF"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="#1F1F1F"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="#EAF3FF"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="#1F1F1F"/>
</DataGrid.Resources>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#F5F7FA"/>
<Setter Property="Foreground" Value="#606266"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Padding" Value="8,0"/>
<Setter Property="BorderBrush" Value="#EBEEF5"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="#262626"/>
<Setter Property="BorderBrush" Value="#FFEDEFF2"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#EAF3FF"/>
<Setter Property="Foreground" Value="#1F1F1F"/>
<Setter Property="BorderBrush" Value="#D6E8FF"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#F5F9FF"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="8,0"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="客户编码" Binding="{Binding CustomerCode}" Width="130"/>
<DataGridTextColumn Header="客户名称" Binding="{Binding CustomerName}" Width="*"/>
<DataGridTextColumn Header="简称" Binding="{Binding CustomerShortName}" Width="100"/>
<DataGridTextColumn Header="状态" Binding="{Binding StatusText}" Width="60"/>
</DataGrid.Columns>
</DataGrid>
<!-- 底部操作栏 -->
<Border Grid.Row="3" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,1,0,0"
Padding="16,12">
<Grid>
<!-- 已选提示 -->
<TextBlock VerticalAlignment="Center" FontSize="13" MaxWidth="320"
TextTrimming="CharacterEllipsis" HorizontalAlignment="Left"
Text="{Binding SelectedCustomerDisplay}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource SecondaryTextBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasSelectedCustomer}" Value="True">
<Setter Property="Foreground" Value="#52c41a"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- 按钮 -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="取 消" Command="{Binding CancelCommand}"
Style="{StaticResource ButtonDefault}" Width="88" Margin="0,0,12,0" Height="34"/>
<Button Content="确认选择" Command="{Binding ConfirmCommand}"
Style="{StaticResource ButtonSuccess}" Width="100" Height="34"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@@ -0,0 +1,19 @@
using System.Windows.Controls;
using System.Windows.Input;
using YY.Admin.ViewModels.WeightRecord;
namespace YY.Admin.Views.WeightRecord;
public partial class CustomerPickerDialogView : UserControl
{
public CustomerPickerDialogView()
{
InitializeComponent();
}
private void CustomersGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (DataContext is CustomerPickerDialogViewModel vm && vm.ConfirmCommand.CanExecute())
vm.ConfirmCommand.Execute();
}
}

View File

@@ -0,0 +1,170 @@
<UserControl x:Class="YY.Admin.Views.WeightRecord.SupplierPickerDialogView"
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"
Width="640">
<Grid Background="{DynamicResource ThirdlyRegionBrush}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="320"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<!-- 标题栏 -->
<hc:SimplePanel Margin="20,16,20,12">
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="32" Height="32" CornerRadius="6" Background="{DynamicResource PrimaryBrush}" Margin="0,0,10,0">
<md:PackIcon Kind="TruckDelivery" Width="18" Height="18" Foreground="White"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<TextBlock Text="选择供应商(发货单位)" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource PrimaryTextBrush}" VerticalAlignment="Center"/>
</StackPanel>
<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="Center"/>
</hc:SimplePanel>
<!-- 搜索栏 -->
<Border Grid.Row="1" Background="{DynamicResource RegionBrush}" Padding="16,10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="80"/>
</Grid.ColumnDefinitions>
<hc:TextBox Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Placeholder="输入供应商名称搜索..."
hc:InfoElement.ShowClearButton="True"
Margin="0,0,8,0">
<hc:TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
</hc:TextBox.InputBindings>
</hc:TextBox>
<Button Grid.Column="1" Command="{Binding SearchCommand}"
IsEnabled="{Binding IsLoading, Converter={StaticResource Boolean2BooleanReConverter}}"
Style="{StaticResource ButtonPrimary}" Height="32">
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="Magnify" Width="14" Height="14" VerticalAlignment="Center" Margin="0,0,4,0"/>
<TextBlock Text="搜索" FontSize="13" VerticalAlignment="Center"/>
</StackPanel>
</Button>
</Grid>
</Border>
<!-- 供应商列表 -->
<DataGrid x:Name="SuppliersGrid"
Grid.Row="2"
Margin="16,8,16,0"
ItemsSource="{Binding Suppliers}"
SelectedItem="{Binding SelectedSupplier}"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserResizeRows="False"
HeadersVisibility="Column"
SelectionMode="Single"
SelectionUnit="FullRow"
IsReadOnly="True"
GridLinesVisibility="Horizontal"
HorizontalGridLinesBrush="#FFEDEFF2"
VerticalGridLinesBrush="Transparent"
Background="White"
RowBackground="White"
AlternatingRowBackground="#FAFCFF"
RowHeight="32"
ColumnHeaderHeight="34"
RowHeaderWidth="0"
MouseDoubleClick="SuppliersGrid_MouseDoubleClick">
<!-- 覆盖系统高亮刷,防止失焦后选中行变白 -->
<DataGrid.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#EAF3FF"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="#1F1F1F"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}" Color="#EAF3FF"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}" Color="#1F1F1F"/>
</DataGrid.Resources>
<DataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="#F5F7FA"/>
<Setter Property="Foreground" Value="#606266"/>
<Setter Property="FontSize" Value="13"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Padding" Value="8,0"/>
<Setter Property="BorderBrush" Value="#EBEEF5"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
</Style>
</DataGrid.ColumnHeaderStyle>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="#262626"/>
<Setter Property="BorderBrush" Value="#FFEDEFF2"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#EAF3FF"/>
<Setter Property="Foreground" Value="#1F1F1F"/>
<Setter Property="BorderBrush" Value="#D6E8FF"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#F5F9FF"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Padding" Value="8,0"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="供应商编码" Binding="{Binding SupplierCode}" Width="130"/>
<DataGridTextColumn Header="供应商名称" Binding="{Binding SupplierName}" Width="*"/>
<DataGridTextColumn Header="简称" Binding="{Binding SupplierShortName}" Width="100"/>
<DataGridTextColumn Header="状态" Binding="{Binding StatusText}" Width="60"/>
</DataGrid.Columns>
</DataGrid>
<!-- 底部操作栏 -->
<Border Grid.Row="3" BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,1,0,0"
Padding="16,12">
<Grid>
<!-- 已选提示 -->
<TextBlock VerticalAlignment="Center" FontSize="13" MaxWidth="320"
TextTrimming="CharacterEllipsis" HorizontalAlignment="Left"
Text="{Binding SelectedSupplierDisplay}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource SecondaryTextBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasSelectedSupplier}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource PrimaryBrush}"/>
<Setter Property="FontWeight" Value="SemiBold"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- 按钮 -->
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="取 消" Command="{Binding CancelCommand}"
Style="{StaticResource ButtonDefault}" Width="88" Margin="0,0,12,0" Height="34"/>
<Button Content="确认选择" Command="{Binding ConfirmCommand}"
Style="{StaticResource ButtonPrimary}" Width="100" Height="34"/>
</StackPanel>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@@ -0,0 +1,19 @@
using System.Windows.Controls;
using System.Windows.Input;
using YY.Admin.ViewModels.WeightRecord;
namespace YY.Admin.Views.WeightRecord;
public partial class SupplierPickerDialogView : UserControl
{
public SupplierPickerDialogView()
{
InitializeComponent();
}
private void SuppliersGrid_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
if (DataContext is SupplierPickerDialogViewModel vm && vm.ConfirmCommand.CanExecute())
vm.ConfirmCommand.Execute();
}
}

View File

@@ -0,0 +1,189 @@
<UserControl x:Class="YY.Admin.Views.WeightRecord.WeightRecordEditDialogView"
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="720"
MinHeight="500">
<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="12">
<hc:TextBox Text="{Binding Record.BillNo, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="磅单号"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="留空则自动生成"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 称重日期 -->
<hc:Col Span="12">
<hc:DatePicker SelectedDate="{Binding Record.WeighDate}"
hc:InfoElement.Title="称重日期"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 进出方向 -->
<hc:Col Span="12">
<hc:ComboBox SelectedValuePath="Value"
DisplayMemberPath="Key"
ItemsSource="{Binding InoutDirectionOptions}"
SelectedValue="{Binding Record.InoutDirection}"
hc:InfoElement.Title="进出方向"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 车牌号 -->
<hc:Col Span="12">
<hc:TextBox Text="{Binding Record.PlateNumber, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="车牌号"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="请输入车牌号"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 发货单位 -->
<hc:Col Span="12">
<hc:TextBox Text="{Binding Record.SenderUnit, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="发货单位"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="进厂时为供应商名称"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 收货单位 -->
<hc:Col Span="12">
<hc:TextBox Text="{Binding Record.ReceiverUnit, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="收货单位"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="出厂时为客户简称"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 货物名称 -->
<hc:Col Span="12">
<hc:TextBox Text="{Binding Record.GoodsName, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="货物名称"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="请输入货物名称"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 毛重 -->
<hc:Col Span="12">
<hc:NumericUpDown Value="{Binding Record.GrossWeight}"
Minimum="0"
DecimalPlaces="2"
Style="{StaticResource NumericUpDownPlus}"
hc:InfoElement.Title="毛重(KG)"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="请输入毛重"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 皮重 -->
<hc:Col Span="12">
<hc:NumericUpDown Value="{Binding Record.TareWeight}"
Minimum="0"
DecimalPlaces="2"
Style="{StaticResource NumericUpDownPlus}"
hc:InfoElement.Title="皮重(KG)"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="请输入皮重"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 净重(只读) -->
<hc:Col Span="12">
<hc:NumericUpDown Value="{Binding Record.NetWeight}"
Minimum="0"
DecimalPlaces="2"
Style="{StaticResource NumericUpDownPlus}"
IsEnabled="True"
hc:InfoElement.Title="净重(KG)"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="自动计算"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 司机 -->
<hc:Col Span="12">
<hc:TextBox Text="{Binding Record.DriverName, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="司机姓名"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="请输入司机姓名"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,16"/>
</hc:Col>
<!-- 手机号 -->
<hc:Col Span="12">
<hc:TextBox Text="{Binding Record.DriverPhone, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="手机号码"
hc:InfoElement.TitleWidth="80"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="请输入手机号"
hc:InfoElement.ShowClearButton="True"
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,8 @@
using System.Windows.Controls;
namespace YY.Admin.Views.WeightRecord;
public partial class WeightRecordEditDialogView : UserControl
{
public WeightRecordEditDialogView() => InitializeComponent();
}

View File

@@ -0,0 +1,195 @@
<UserControl x:Class="YY.Admin.Views.WeightRecord.WeightRecordListView"
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=4, Xl=4}">
<hc:TextBox Text="{Binding FilterBillNo, 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:TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
</hc:TextBox.InputBindings>
</hc:TextBox>
</hc:Col>
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=4, Xl=4}">
<hc:TextBox Text="{Binding FilterPlateNumber, 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:TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
</hc:TextBox.InputBindings>
</hc:TextBox>
</hc:Col>
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=4, Xl=4}">
<hc:ComboBox SelectedValuePath="Value"
DisplayMemberPath="Key"
ItemsSource="{Binding InoutDirectionOptions}"
SelectedValue="{Binding FilterInoutDirection}"
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=4, Xl=4}">
<hc:TextBox Text="{Binding FilterGoodsName, 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:TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
</hc:TextBox.InputBindings>
</hc:TextBox>
</hc:Col>
<hc:Col Layout="{hc:ColLayout Xs=12, Sm=8, Md=6, Lg=4, Xl=4}">
<hc:TextBox Text="{Binding FilterDriverName, 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:TextBox.InputBindings>
<KeyBinding Key="Enter" Command="{Binding SearchCommand}"/>
</hc:TextBox.InputBindings>
</hc:TextBox>
</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 Records}"
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 BillNo}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="130"/>
<DataGridTextColumn Header="称重日期" Binding="{Binding WeighDate, StringFormat='yyyy-MM-dd'}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="110"/>
<DataGridTextColumn Header="进出方向" Binding="{Binding InoutDirectionText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="90"/>
<DataGridTextColumn Header="单据类型" Binding="{Binding BillTypeText}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
<DataGridTextColumn Header="车牌号" Binding="{Binding PlateNumber}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="110"/>
<DataGridTextColumn Header="发货单位" Binding="{Binding SenderUnit}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
<DataGridTextColumn Header="收货单位" Binding="{Binding ReceiverUnit}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="140"/>
<DataGridTextColumn Header="货物名称" Binding="{Binding GoodsName}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="120"/>
<DataGridTextColumn Header="毛重(KG)" Binding="{Binding GrossWeight, StringFormat=N2}" CellStyle="{StaticResource CusDataGridCellStyle}" Width="100"/>
<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="司机" 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"/>
<DataGridTemplateColumn Header="操作" Width="150" 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,8 @@
using System.Windows.Controls;
namespace YY.Admin.Views.WeightRecord;
public partial class WeightRecordListView : UserControl
{
public WeightRecordListView() => InitializeComponent();
}

View File

@@ -0,0 +1,767 @@
<UserControl x:Class="YY.Admin.Views.WeightRecord.WeightRecordOperationView"
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">
<UserControl.Resources>
<!-- 深色工业风面板背景 -->
<SolidColorBrush x:Key="PanelDarkBrush" Color="#1a1a2e"/>
<SolidColorBrush x:Key="WeightGreenBrush" Color="#00e676"/>
<SolidColorBrush x:Key="WeightOrangeBrush" Color="#ff9800"/>
<SolidColorBrush x:Key="SerialConnectedBrush" Color="#4caf50"/>
<SolidColorBrush x:Key="SerialDisconnectedBrush" Color="#f44336"/>
<SolidColorBrush x:Key="SectionBorderBrush" Color="#1890ff"/>
<!-- 分组标题样式(左侧蓝色色条) -->
<Style x:Key="SectionTitleStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="14"/>
<Setter Property="FontWeight" Value="SemiBold"/>
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
<Setter Property="Margin" Value="10,0,0,0"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
<!-- 分组容器样式 -->
<Style x:Key="SectionBorderStyle" TargetType="Border">
<Setter Property="CornerRadius" Value="8"/>
<Setter Property="Background" Value="{DynamicResource ThirdlyRegionBrush}"/>
<Setter Property="Padding" Value="16"/>
<Setter Property="Margin" Value="0,0,0,12"/>
</Style>
<!-- 采集按钮样式 -->
<Style x:Key="CaptureButtonStyle" TargetType="Button" BasedOn="{StaticResource ButtonSuccess}">
<Setter Property="Height" Value="36"/>
<Setter Property="Padding" Value="16,0"/>
<Setter Property="FontSize" Value="13"/>
</Style>
<Style x:Key="CaptureGrossButtonStyle" TargetType="Button" BasedOn="{StaticResource ButtonPrimary}">
<Setter Property="Height" Value="36"/>
<Setter Property="Padding" Value="16,0"/>
<Setter Property="FontSize" Value="13"/>
</Style>
<!-- 已采集标签样式 -->
<Style x:Key="CapturedTagStyle" TargetType="Border">
<Setter Property="Background" Value="#4caf50"/>
<Setter Property="CornerRadius" Value="4"/>
<Setter Property="Padding" Value="10,4"/>
<Setter Property="Height" Value="36"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</UserControl.Resources>
<Grid>
<Grid.RowDefinitions>
<!-- 标题区 -->
<RowDefinition Height="70"/>
<!-- 主体区 -->
<RowDefinition Height="*"/>
<!-- 操作栏 -->
<RowDefinition Height="64"/>
</Grid.RowDefinitions>
<!-- ══════════════════════ Row 0: 页面标题区 ══════════════════════ -->
<Border Grid.Row="0" Background="{DynamicResource RegionBrush}"
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,0,0,1">
<Grid Margin="20,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<!-- 左侧标题 -->
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center">
<Border Width="40" Height="40" CornerRadius="8" Background="{DynamicResource PrimaryBrush}" Margin="0,0,12,0">
<md:PackIcon Kind="ScaleBalance" Width="22" Height="22" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<StackPanel VerticalAlignment="Center">
<TextBlock Text="地磅称重操作" FontSize="18" FontWeight="Bold" Foreground="{DynamicResource PrimaryTextBrush}"/>
<TextBlock Text="实时称重数据录入" FontSize="12" Foreground="{DynamicResource SecondaryTextBrush}"/>
</StackPanel>
</StackPanel>
<!-- 右侧状态 -->
<StackPanel Grid.Column="2" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,8,0">
<!-- 串口状态 -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,20,0">
<Ellipse Width="10" Height="10" VerticalAlignment="Center" Margin="0,0,6,0">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="{StaticResource SerialDisconnectedBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsSerialConnected}" Value="True">
<Setter Property="Fill" Value="{StaticResource SerialConnectedBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<TextBlock Text="{Binding SerialStatusText}" FontSize="13"
Foreground="{DynamicResource SecondaryTextBrush}" VerticalAlignment="Center"/>
</StackPanel>
</StackPanel>
</Grid>
</Border>
<!-- ══════════════════════ Row 1: 主体区(左监控 + 右表单) ══════════════════════ -->
<Grid Grid.Row="1" Margin="16,12,16,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2.2*"/>
<ColumnDefinition Width="12"/>
<ColumnDefinition Width="3*"/>
</Grid.ColumnDefinitions>
<!-- ───── 左侧:实时监控面板 ───── -->
<ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Auto">
<StackPanel>
<!-- 实时重量显示 -->
<Border Style="{StaticResource SectionBorderStyle}">
<StackPanel>
<!-- 分组标题 -->
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border Width="4" Height="18" CornerRadius="2" Background="{StaticResource SectionBorderBrush}"/>
<TextBlock Text="实时重量" Style="{StaticResource SectionTitleStyle}"/>
</StackPanel>
<!-- 大屏重量显示 -->
<Border CornerRadius="12" Background="{StaticResource PanelDarkBrush}" Padding="16,20">
<StackPanel HorizontalAlignment="Center">
<!-- 重量数字 -->
<TextBlock Text="{Binding CurrentWeightDisplay}" FontFamily="Consolas"
FontSize="52" FontWeight="Bold" HorizontalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource WeightGreenBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsWeightStable}" Value="False">
<Setter Property="Foreground" Value="{StaticResource WeightOrangeBrush}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsSerialConnected}" Value="False">
<Setter Property="Foreground" Value="#666"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Text="kg" FontFamily="Consolas" FontSize="22" HorizontalAlignment="Center"
Foreground="#888" Margin="0,0,0,8"/>
<!-- 稳定状态 -->
<Border CornerRadius="12" Padding="12,4" HorizontalAlignment="Center">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="#33ff9800"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsWeightStable}" Value="True">
<Setter Property="Background" Value="#3300e676"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<StackPanel Orientation="Horizontal">
<Ellipse Width="8" Height="8" VerticalAlignment="Center" Margin="0,0,6,0">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="{StaticResource WeightOrangeBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsWeightStable}" Value="True">
<Setter Property="Fill" Value="{StaticResource WeightGreenBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<TextBlock VerticalAlignment="Center" FontSize="13">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="数据波动中"/>
<Setter Property="Foreground" Value="{StaticResource WeightOrangeBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsWeightStable}" Value="True">
<Setter Property="Text" Value="数据稳定"/>
<Setter Property="Foreground" Value="{StaticResource WeightGreenBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</StackPanel>
</Border>
</StackPanel>
</Border>
</StackPanel>
</Border>
<!-- 车牌识别 -->
<Border Style="{StaticResource SectionBorderStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,12">
<Border Width="4" Height="18" CornerRadius="2" Background="{StaticResource SectionBorderBrush}"/>
<TextBlock Text="车牌识别" Style="{StaticResource SectionTitleStyle}"/>
</StackPanel>
<!-- 车牌显示区 -->
<Border CornerRadius="8" Padding="12,10" MinHeight="60">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="{DynamicResource RegionBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasPlate}" Value="True">
<Setter Property="Background" Value="#0D1890FF"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid>
<!-- 未识别提示 -->
<TextBlock Text="等待车辆进入识别区域..." FontSize="13"
Foreground="{DynamicResource SecondaryTextBrush}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasPlate}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<!-- 车牌样式(仿中国车牌) -->
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasPlate}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<Border Background="#003880" CornerRadius="6" Padding="16,8">
<TextBlock Text="{Binding DetectedPlate}" FontSize="28" FontWeight="Bold"
Foreground="White" FontFamily="Consolas"
HorizontalAlignment="Center"/>
</Border>
<Button Content="使用此车牌" Command="{Binding UseDetectedPlateCommand}"
Style="{StaticResource ButtonPrimary}"
Height="32" Margin="0,8,0,0" FontSize="12"/>
</StackPanel>
</Grid>
</Border>
</StackPanel>
</Border>
<!-- 操作日志 -->
<Border Style="{StaticResource SectionBorderStyle}">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,10">
<Border Width="4" Height="18" CornerRadius="2" Background="{StaticResource SectionBorderBrush}"/>
<TextBlock Text="操作日志" Style="{StaticResource SectionTitleStyle}"/>
</StackPanel>
<ItemsControl ItemsSource="{Binding OperationLogs}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontSize="12" Foreground="{DynamicResource SecondaryTextBrush}"
Margin="0,2" FontFamily="Consolas" TextWrapping="NoWrap"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Border>
</StackPanel>
</ScrollViewer>
<!-- ───── 分隔线 ───── -->
<Border Grid.Column="1" Width="1" Background="{DynamicResource BorderBrush}" Margin="0,0,0,0"/>
<!-- ───── 右侧:信息录入表单 ───── -->
<Grid Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!-- 基本信息 -->
<Border Grid.Row="0" Style="{StaticResource SectionBorderStyle}" Margin="0,0,0,6" Padding="12,10">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,6">
<Border Width="4" Height="18" CornerRadius="2" Background="{StaticResource SectionBorderBrush}"/>
<TextBlock Text="基本信息" Style="{StaticResource SectionTitleStyle}"/>
</StackPanel>
<hc:Row Gutter="10">
<!-- 称重日期 -->
<hc:Col Span="12">
<hc:DatePicker SelectedDate="{Binding WeighDate}"
hc:InfoElement.Title="称重日期"
hc:InfoElement.TitleWidth="75"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
Margin="0,0,0,8"/>
</hc:Col>
<!-- 进出方向 -->
<hc:Col Span="12">
<hc:ComboBox SelectedValuePath="Value"
DisplayMemberPath="Key"
ItemsSource="{Binding InoutDirectionOptions}"
SelectedValue="{Binding InoutDirection}"
hc:InfoElement.Title="进出方向"
hc:InfoElement.TitleWidth="75"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
Margin="0,0,0,8"/>
</hc:Col>
<!-- 车牌号 -->
<hc:Col Span="24">
<hc:TextBox Text="{Binding PlateNumber, UpdateSourceTrigger=PropertyChanged}"
IsReadOnly="{Binding IsPlateNumberLocked}"
hc:InfoElement.Title="车牌号"
hc:InfoElement.TitleWidth="75"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="手动输入或点击「使用此车牌」"
hc:InfoElement.Necessary="True"
hc:InfoElement.Symbol="*"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,8"/>
</hc:Col>
<!-- 车辆档案匹配状态 -->
<hc:Col Span="24">
<hc:Col.Style>
<Style TargetType="hc:Col">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ShowVehicleMatchHint}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</hc:Col.Style>
<Border CornerRadius="4" Padding="10,5" Margin="0,-6,0,8">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding VehicleLookupStatus}" Value="Matched">
<Setter Property="Background" Value="#0D4caf50"/>
</DataTrigger>
<DataTrigger Binding="{Binding VehicleLookupStatus}" Value="NotFound">
<Setter Property="Background" Value="#0Dff9800"/>
</DataTrigger>
<DataTrigger Binding="{Binding VehicleLookupStatus}" Value="Searching">
<Setter Property="Background" Value="#0D1890ff"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding VehicleMatchText}" FontSize="12">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource SecondaryTextBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding VehicleLookupStatus}" Value="Matched">
<Setter Property="Foreground" Value="#4caf50"/>
</DataTrigger>
<DataTrigger Binding="{Binding VehicleLookupStatus}" Value="NotFound">
<Setter Property="Foreground" Value="#ff9800"/>
</DataTrigger>
<DataTrigger Binding="{Binding VehicleLookupStatus}" Value="Searching">
<Setter Property="Foreground" Value="#1890ff"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
</hc:Col>
<!-- 发货单位(供应商弹窗选择) -->
<hc:Col Span="24">
<hc:Col.Style>
<Style TargetType="hc:Col">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding InoutDirection}" Value="2">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</hc:Col.Style>
<Grid Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="32"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="发货单位" VerticalAlignment="Center"
FontSize="14" Foreground="{DynamicResource PrimaryTextBrush}"/>
<Border Grid.Column="1" CornerRadius="4" Height="32"
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"
Background="{DynamicResource ThirdlyRegionBrush}">
<TextBlock Text="{Binding SenderUnitDisplay}" VerticalAlignment="Center"
Margin="8,0" FontSize="13" TextTrimming="CharacterEllipsis">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource SecondaryTextBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasSelectedSupplier}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
<Button Grid.Column="2" Content="选 择"
Command="{Binding OpenSupplierPickerCommand}"
Style="{StaticResource ButtonPrimary}"
Height="32" Margin="6,0,0,0" FontSize="12"/>
<Button Grid.Column="3" Command="{Binding ClearSupplierCommand}"
Style="{StaticResource ButtonIcon}"
Height="32" Width="28" Margin="4,0,0,0" Padding="0"
ToolTip="清除选择"
Foreground="{DynamicResource SecondaryTextBrush}"
hc:IconElement.Geometry="{StaticResource ErrorGeometry}"/>
</Grid>
</hc:Col>
<!-- 收货单位(客户弹窗选择) -->
<hc:Col Span="24">
<hc:Col.Style>
<Style TargetType="hc:Col">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding InoutDirection}" Value="2">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</hc:Col.Style>
<Grid Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="60"/>
<ColumnDefinition Width="32"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="收货单位" VerticalAlignment="Center"
FontSize="14" Foreground="{DynamicResource PrimaryTextBrush}"/>
<Border Grid.Column="1" CornerRadius="4" Height="32"
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="1"
Background="{DynamicResource ThirdlyRegionBrush}">
<TextBlock Text="{Binding ReceiverUnitDisplay}" VerticalAlignment="Center"
Margin="8,0" FontSize="13" TextTrimming="CharacterEllipsis">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource SecondaryTextBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding HasSelectedCustomer}" Value="True">
<Setter Property="Foreground" Value="{DynamicResource PrimaryTextBrush}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
<Button Grid.Column="2" Content="选 择"
Command="{Binding OpenCustomerPickerCommand}"
Style="{StaticResource ButtonSuccess}"
Height="32" Margin="6,0,0,0" FontSize="12"/>
<Button Grid.Column="3" Command="{Binding ClearCustomerCommand}"
Style="{StaticResource ButtonIcon}"
Height="32" Width="28" Margin="4,0,0,0" Padding="0"
ToolTip="清除选择"
Foreground="{DynamicResource SecondaryTextBrush}"
hc:IconElement.Geometry="{StaticResource ErrorGeometry}"/>
</Grid>
</hc:Col>
<!-- 司机 -->
<hc:Col Span="12">
<hc:TextBox Text="{Binding DriverName, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="司机姓名"
hc:InfoElement.TitleWidth="75"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="司机姓名"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,0"/>
</hc:Col>
<!-- 手机号 -->
<hc:Col Span="12">
<hc:TextBox Text="{Binding DriverPhone, UpdateSourceTrigger=PropertyChanged}"
hc:InfoElement.Title="手机号码"
hc:InfoElement.TitleWidth="75"
hc:InfoElement.TitlePlacement="Left"
hc:InfoElement.Placeholder="手机号码"
hc:InfoElement.ShowClearButton="True"
Margin="0,0,0,0"/>
</hc:Col>
</hc:Row>
</StackPanel>
</Border>
<!-- 重量信息 -->
<Border Grid.Row="1" Style="{StaticResource SectionBorderStyle}" Margin="0,0,0,0" Padding="12,10">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,4">
<Border Width="4" Height="18" CornerRadius="2" Background="{StaticResource SectionBorderBrush}"/>
<TextBlock Text="重量信息" Style="{StaticResource SectionTitleStyle}"/>
</StackPanel>
<!-- 毛重 / 皮重并排 -->
<Grid Margin="0,0,0,8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="16"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 毛重 -->
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Grid.Column" Value="0"/>
<Style.Triggers>
<DataTrigger Binding="{Binding InoutDirection}" Value="2">
<Setter Property="Grid.Column" Value="2"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Text="毛 重KG" FontSize="12" Foreground="{DynamicResource SecondaryTextBrush}"
Margin="0,0,0,4" HorizontalAlignment="Center"/>
<Border CornerRadius="8" Padding="10,10" Background="{DynamicResource RegionBrush}">
<TextBlock Text="{Binding GrossWeight, StringFormat=N2, FallbackValue=—, TargetNullValue=—}"
HorizontalAlignment="Center" FontSize="20" FontWeight="Bold"
FontFamily="Consolas">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource SecondaryTextBrush}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding GrossWeightCaptured}" Value="True">
<Setter Property="Foreground" Value="#4caf50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Border>
<Border Margin="0,6,0,0" Height="32" CornerRadius="6">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding GrossWeightCaptured}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Background" Value="#1A4caf50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="✓ 已采集" Foreground="#4caf50" FontSize="13"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Button Command="{Binding CaptureGrossWeightCommand}"
Margin="0,6,0,0"
HorizontalAlignment="Stretch">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource CaptureGrossButtonStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding GrossWeightCaptured}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="Truck" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Text="{Binding CaptureGrossButtonText}" VerticalAlignment="Center"/>
</StackPanel>
</Button>
</StackPanel>
<!-- 皮重 -->
<StackPanel>
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Grid.Column" Value="2"/>
<Style.Triggers>
<DataTrigger Binding="{Binding InoutDirection}" Value="2">
<Setter Property="Grid.Column" Value="0"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Text="皮 重KG" FontSize="12" Foreground="{DynamicResource SecondaryTextBrush}"
Margin="0,0,0,4" HorizontalAlignment="Center"/>
<Border CornerRadius="8" Padding="10,10" Background="{DynamicResource RegionBrush}">
<TextBlock HorizontalAlignment="Center" FontSize="20" FontWeight="Bold"
FontFamily="Consolas" Foreground="{DynamicResource SecondaryTextBrush}">
<TextBlock.Text>
<Binding Path="TareWeight" StringFormat="N2" FallbackValue="—" TargetNullValue="—"/>
</TextBlock.Text>
</TextBlock>
</Border>
<Border Margin="0,6,0,0" Height="32" CornerRadius="6">
<Border.Style>
<Style TargetType="Border">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding TareWeightCaptured}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
<Setter Property="Background" Value="#1A4caf50"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="✓ 已采集" Foreground="#4caf50" FontSize="13"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<Button Command="{Binding CaptureTareWeightCommand}"
Margin="0,6,0,0"
HorizontalAlignment="Stretch">
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource CaptureButtonStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding TareWeightCaptured}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="TruckOutline" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Text="{Binding CaptureTareButtonText}" VerticalAlignment="Center"/>
</StackPanel>
</Button>
</StackPanel>
</Grid>
<!-- 净重展示(横贯全宽) -->
<StackPanel Margin="0,2,0,0">
<TextBlock Text="净 重 = 毛重 - 皮重 KG" FontSize="12"
Foreground="{DynamicResource SecondaryTextBrush}"
Margin="0,0,0,6" HorizontalAlignment="Center"/>
<Border CornerRadius="10" Padding="12,10" Background="#0D52c41a"
BorderBrush="#52c41a" BorderThickness="1.5">
<TextBlock Text="{Binding NetWeightDisplay}" FontSize="28" FontWeight="Bold"
FontFamily="Consolas" HorizontalAlignment="Center"
Foreground="#52c41a"/>
</Border>
</StackPanel>
<!-- 最近称重榜单 -->
<StackPanel Margin="0,8,0,0">
<TextBlock Text="最近称重榜单"
FontSize="12"
Foreground="{DynamicResource SecondaryTextBrush}"
Margin="0,0,0,6"/>
<Border CornerRadius="8"
Background="White"
BorderBrush="{DynamicResource BorderBrush}"
BorderThickness="1"
Padding="8,6">
<DataGrid ItemsSource="{Binding RecentWeightRecords}"
SelectedItem="{Binding SelectedRecentWeightRecord}"
AutoGenerateColumns="False"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserResizeRows="False"
HeadersVisibility="Column"
SelectionMode="Single"
SelectionUnit="FullRow"
IsReadOnly="True"
GridLinesVisibility="Horizontal"
HorizontalGridLinesBrush="#FFEDEFF2"
VerticalGridLinesBrush="Transparent"
Background="White"
RowBackground="White"
AlternatingRowBackground="White"
RowHeight="26"
ColumnHeaderHeight="28"
RowHeaderWidth="0"
MinHeight="80"
MaxHeight="80">
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="White"/>
<Setter Property="Foreground" Value="#262626"/>
<Setter Property="BorderBrush" Value="#FFEDEFF2"/>
<Setter Property="BorderThickness" Value="0,0,0,1"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#FFEAF3FF"/>
<Setter Property="Foreground" Value="#1F1F1F"/>
<Setter Property="BorderBrush" Value="#FFD6E8FF"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="Foreground" Value="#262626"/>
<Setter Property="Background" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#FFEAF3FF"/>
<Setter Property="Foreground" Value="#1F1F1F"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<DataGrid.Columns>
<DataGridTextColumn Header="榜单号" Binding="{Binding BillNo}" Width="2.2*"/>
<DataGridTextColumn Header="车辆" Binding="{Binding PlateNumber}" Width="1.3*"/>
<DataGridTextColumn Header="首称重量(KG)" Binding="{Binding FirstWeightDisplay}" Width="1*"/>
</DataGrid.Columns>
</DataGrid>
</Border>
</StackPanel>
</StackPanel>
</Border>
</Grid>
</Grid>
<!-- ══════════════════════ Row 2: 底部操作栏 ══════════════════════ -->
<Border Grid.Row="2" Background="{DynamicResource RegionBrush}"
BorderBrush="{DynamicResource BorderBrush}" BorderThickness="0,1,0,0">
<Grid Margin="20,0">
<!-- 清空按钮(左) -->
<Button HorizontalAlignment="Left" VerticalAlignment="Center"
Command="{Binding ClearCommand}"
Style="{StaticResource ButtonDefault}"
Height="40" Padding="20,0">
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="TrashCanOutline" VerticalAlignment="Center" Margin="0,0,6,0"/>
<TextBlock Text="清空表单" VerticalAlignment="Center"/>
</StackPanel>
</Button>
<!-- 保存按钮(右) -->
<Button HorizontalAlignment="Right" VerticalAlignment="Center"
Command="{Binding SaveCommand}"
Style="{StaticResource ButtonSuccess}"
Height="40" Padding="28,0" FontSize="15">
<StackPanel Orientation="Horizontal">
<md:PackIcon Kind="ContentSave" VerticalAlignment="Center" Margin="0,0,8,0" Width="20" Height="20"/>
<TextBlock Text="保 存 记 录" VerticalAlignment="Center" FontWeight="SemiBold"/>
</StackPanel>
</Button>
</Grid>
</Border>
</Grid>
</UserControl>

View File

@@ -0,0 +1,8 @@
using System.Windows.Controls;
namespace YY.Admin.Views.WeightRecord;
public partial class WeightRecordOperationView : UserControl
{
public WeightRecordOperationView() => InitializeComponent();
}