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);
// 操作人员通过弹窗选了供应商,且与档案不同(含原来为空的情况)
bool supplierChanged = _matchedVehicle.VehicleBelong == "2"
&& _selectedSupplier != null
&& _selectedSupplier.Id != _matchedVehicle.SupplierId;
// 操作人员通过弹窗选了客户,且与档案不同(含原来为空的情况)
bool customerChanged = _matchedVehicle.VehicleBelong == "1"
&& _selectedCustomer != null
&& _selectedCustomer.Id != _matchedVehicle.CustomerIds;
if (!driverChanged && !supplierChanged && !customerChanged) return;
await _vehicleService.EditAsync(new MesXslVehicle
{
Id = _matchedVehicle.Id,
PlateNumber = _matchedVehicle.PlateNumber,
VehicleBelong = _matchedVehicle.VehicleBelong,
TareWeightKg = _matchedVehicle.TareWeightKg,
LoadCapacity = _matchedVehicle.LoadCapacity,
SupplierId = supplierChanged ? _selectedSupplier!.Id : _matchedVehicle.SupplierId,
SupplierName = supplierChanged ? _selectedSupplier!.SupplierName : _matchedVehicle.SupplierName,
SupplierShortName = supplierChanged ? _selectedSupplier!.SupplierShortName : _matchedVehicle.SupplierShortName,
CustomerIds = customerChanged ? _selectedCustomer!.Id : _matchedVehicle.CustomerIds,
CustomerShortName = customerChanged
? (_selectedCustomer!.CustomerShortName ?? _selectedCustomer.CustomerName)
: _matchedVehicle.CustomerShortName,
DriverName = DriverName,
DriverPhone = DriverPhone,
Status = _matchedVehicle.Status,
TenantId = _matchedVehicle.TenantId,
Version = _matchedVehicle.Version,
});
var parts = new List();
if (driverChanged) parts.Add("司机信息");
if (supplierChanged) parts.Add($"供应商→{_selectedSupplier!.SupplierName}");
if (customerChanged) parts.Add($"客户→{_selectedCustomer!.CustomerName}");
AddLog($"车辆档案已更新:{PlateNumber}({string.Join("、", parts)})");
}
else
{
// 只有明确查询过且未找到时才新建,避免从最近榜单带入时重复新建
if (VehicleLookupStatus != "NotFound") return;
var vehicleBelong = InoutDirection == "2" ? "1" : "2"; // 出厂→客户 进厂→供应商
var newVehicle = new MesXslVehicle
{
PlateNumber = PlateNumber?.Trim(),
VehicleBelong = vehicleBelong,
SupplierId = _selectedSupplier?.Id,
SupplierName = _selectedSupplier?.SupplierName,
SupplierShortName = _selectedSupplier?.SupplierShortName,
CustomerIds = _selectedCustomer?.Id,
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; } = "-";
}