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; /// /// 地磅称重操作页面 ViewModel /// 左侧:串口模拟实时数据(重量 + 车牌);右侧:称重信息录入表单。 /// 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 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> InoutDirectionOptions { get; } = new(); public ObservableCollection 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() .Initialize(vm => { pickerVm = vm; }) .GetResultAsync(); } 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() .Initialize(vm => { pickerVm = vm; }) .GetResultAsync(); } 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("进厂", "1")); InoutDirectionOptions.Add(new KeyValuePair("出厂", "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; } = "-"; }