using HandyControl.Controls; using LiveChartsCore; using LiveChartsCore.Defaults; using LiveChartsCore.SkiaSharpView; using LiveChartsCore.SkiaSharpView.Painting; using Prism.Events; using Prism.Mvvm; using Prism.Navigation; using SkiaSharp; using System.Collections.ObjectModel; using System.Threading; using YY.Admin.Core; using YY.Admin.Core.Entity; using YY.Admin.Core.Events; using YY.Admin.Core.Services; using YY.Admin.Core.Session; namespace YY.Admin.ViewModels.RubberQuickTest; public class QuickTestInspectCellViewModel : BindableBase { public string? DataPointId { get; set; } public string PointName { get; set; } = string.Empty; public decimal? LowerLimit { get; set; } public decimal? UpperLimit { get; set; } private decimal? _value; public decimal? Value { get => _value; set { if (SetProperty(ref _value, value)) ValueChanged?.Invoke(this); } } public event Action? ValueChanged; } public class QuickTestInspectRowViewModel : BindableBase { private string _rowNo = string.Empty; public string RowNo { get => _rowNo; set => SetProperty(ref _rowNo, value); } public ObservableCollection Cells { get; } = new(); private string _inspectResultText = string.Empty; public string InspectResultText { get => _inspectResultText; set => SetProperty(ref _inspectResultText, value); } public string InspectResultCode { get; private set; } = "0"; public void RecalculateResult() { if (Cells.Count == 0) { InspectResultCode = "0"; InspectResultText = string.Empty; RaisePropertyChanged(nameof(InspectResultText)); return; } if (Cells.Any(c => c.Value == null)) { InspectResultCode = "0"; InspectResultText = "待填写"; RaisePropertyChanged(nameof(InspectResultText)); return; } var pass = Cells.All(c => { if (c.LowerLimit != null && c.Value < c.LowerLimit) return false; if (c.UpperLimit != null && c.Value > c.UpperLimit) return false; return true; }); InspectResultCode = pass ? "1" : "0"; InspectResultText = pass ? "合格" : "不合格"; RaisePropertyChanged(nameof(InspectResultText)); } } /// 胶料快检记录操作台 ViewModel(密炼计划、实验标准均读本地缓存) public class RubberQuickTestOperationViewModel : BaseViewModel, INavigationAware { private readonly IRubberQuickTestRecordService _recordService; private readonly IMixingProductionPlanService _planService; private readonly IRubberQuickTestStdService _stdService; private readonly Random _rnd = new(); private bool _saveInProgress; private string? _loadedDetailLocalId; private string? _loadedDetailMesId; private int _navigationApplyVersion; private List _allPlans = new(); private List _allStds = new(); private MesXslRubberQuickTestStd? _currentStd; private SubscriptionToken? _planChangedToken; private SubscriptionToken? _stdChangedToken; private const int ChartPointCount = 5; /// 曲线横坐标时间点(min):0、0.5、1、1.5、2 private static readonly double[] ChartTimeMinutes = { 0, 0.5, 1, 1.5, 2 }; public event Action? InspectColumnsChanged; public RubberQuickTestOperationViewModel( IRubberQuickTestRecordService recordService, IMixingProductionPlanService planService, IRubberQuickTestStdService stdService, IContainerExtension container, IRegionManager regionManager) : base(container, regionManager) { _recordService = recordService; _planService = planService; _stdService = stdService; _inspectTimesText = "1"; AddInspectRowCommand = new DelegateCommand(AddInspectRow, () => DataPointColumns.Count > 0); RemoveInspectRowCommand = new DelegateCommand(() => RemoveInspectRow(SelectedInspectRow), () => SelectedInspectRow != null); SaveCommand = new DelegateCommand(async () => await SaveAsync(), () => CanSave); RefreshPlansCommand = new DelegateCommand(async () => await LoadLocalDataAsync(showSuccess: true)); RefreshChartDemoCommand = new DelegateCommand(FillRandomChartData); TemperatureSeries = new ObservableCollection { new LineSeries { Name = "上模温度", Values = UpperTempValues, GeometrySize = 4, LineSmoothness = 0.3 }, new LineSeries { Name = "下模温度", Values = LowerTempValues, GeometrySize = 4, LineSmoothness = 0.3 } }; TorqueSeries = new ObservableCollection { new LineSeries { Name = "S'(dNm)", Values = TorqueValues, GeometrySize = 4, LineSmoothness = 0.3 } }; FillRandomChartData(); InspectorDisplay = ResolveInspectorDisplay(); _planChangedToken = _eventAggregator.GetEvent() .Subscribe(async _ => await ReloadLocalDataQuietAsync(), ThreadOption.UIThread); _stdChangedToken = _eventAggregator.GetEvent() .Subscribe(async _ => await ReloadLocalDataQuietAsync(), ThreadOption.UIThread); RecalculateOverallInspectResult(); } private bool _isReadOnly; public bool IsReadOnly { get => _isReadOnly; private set { if (!SetProperty(ref _isReadOnly, value)) return; RaisePropertyChanged(nameof(IsEditable)); RaisePropertyChanged(nameof(ShowSaveButton)); NotifySaveStateChanged(); InspectColumnsChanged?.Invoke(); } } public bool IsEditable => !IsReadOnly; public bool ShowSaveButton => !IsReadOnly; public bool CanSave => InspectRows.Count > 0 && !_saveInProgress && !IsReadOnly; private void NotifySaveStateChanged() { RaisePropertyChanged(nameof(CanSave)); SaveCommand.RaiseCanExecuteChanged(); } protected override void OnIsLoadingChanged() { base.OnIsLoadingChanged(); NotifySaveStateChanged(); } public void OnNavigatedTo(NavigationContext navigationContext) { _ = ApplyNavigationAsync(navigationContext.Parameters); } private async Task ApplyNavigationAsync(INavigationParameters parameters) { var version = Interlocked.Increment(ref _navigationApplyVersion); if (parameters.TryGetValue("readOnly", out var readOnly) && readOnly) { if (parameters.TryGetValue("localId", out var localId) && !string.IsNullOrWhiteSpace(localId)) { _loadedDetailLocalId = localId; _loadedDetailMesId = null; await LoadFromLocalItemAsync(localId); return; } if (parameters.TryGetValue("mesId", out var mesId) && !string.IsNullOrWhiteSpace(mesId)) { _loadedDetailMesId = mesId; _loadedDetailLocalId = null; await LoadFromMesIdAsync(mesId); return; } } if (version != _navigationApplyVersion) return; _loadedDetailLocalId = null; _loadedDetailMesId = null; IsReadOnly = false; ResetFormForNewEntry(); await LoadLocalDataAsync(showSuccess: false); } public bool IsNavigationTarget(NavigationContext navigationContext) { var parameters = navigationContext.Parameters; if (parameters.TryGetValue("readOnly", out var readOnly) && readOnly) { if (parameters.TryGetValue("localId", out var localId) && !string.IsNullOrWhiteSpace(localId)) return IsReadOnly && string.Equals(_loadedDetailLocalId, localId, StringComparison.OrdinalIgnoreCase); if (parameters.TryGetValue("mesId", out var mesId) && !string.IsNullOrWhiteSpace(mesId)) return IsReadOnly && string.Equals(_loadedDetailMesId, mesId, StringComparison.OrdinalIgnoreCase); return false; } return !IsReadOnly && _loadedDetailLocalId == null && _loadedDetailMesId == null; } private void ResetFormForNewEntry() { RecordNo = null; TrainNo = null; InspectTimesText = "1"; ClearPlanAndStdSelection(); RecalculateOverallInspectResult(); NotifySaveStateChanged(); } public void OnNavigatedFrom(NavigationContext navigationContext) { } private static string ResolveInspectorDisplay() { var user = AppSession.CurrentUser; if (user == null) return string.Empty; if (!string.IsNullOrWhiteSpace(user.RealName)) return user.RealName.Trim(); if (!string.IsNullOrWhiteSpace(user.Account)) return user.Account.Trim(); return string.Empty; } /// 中间库未接入时的曲线演示说明 public string ChartDemoHint => "演示数据(中间库未接入,打开页面自动生成;接入后将显示实测曲线)"; public ObservableCollection TemperatureSeries { get; } public ObservableCollection TorqueSeries { get; } public ObservableCollection UpperTempValues { get; } = new(); public ObservableCollection LowerTempValues { get; } = new(); public ObservableCollection TorqueValues { get; } = new(); public Axis[] TemperatureXAxes { get; } = BuildTimeAxis(); public Axis[] TemperatureYAxes { get; } = BuildTempYAxis(); public Axis[] TorqueXAxes { get; } = BuildTimeAxis(); public Axis[] TorqueYAxes { get; } = BuildTorqueYAxis(); public ObservableCollection MachineOptions { get; } = new(); public ObservableCollection ShiftOptions { get; } = new(); public ObservableCollection PlanOptions { get; } = new(); public ObservableCollection StdOptions { get; } = new(); public ObservableCollection DataPointColumns { get; } = new(); public ObservableCollection InspectRows { get; } = new(); private QuickTestInspectRowViewModel? _selectedInspectRow; public QuickTestInspectRowViewModel? SelectedInspectRow { get => _selectedInspectRow; set { if (SetProperty(ref _selectedInspectRow, value)) RemoveInspectRowCommand.RaiseCanExecuteChanged(); } } private DateTime _mixingDate = DateTime.Today; public DateTime MixingDate { get => _mixingDate; set { if (!SetProperty(ref _mixingDate, value)) return; RefreshMachineOptions(); ClearPlanAndStdSelection(); } } private string? _selectedMachine; public string? SelectedMachine { get => _selectedMachine; set { if (!SetProperty(ref _selectedMachine, value)) return; RefreshShiftOptions(); ClearPlanAndStdSelection(); } } private WorkShiftOption? _selectedShift; public WorkShiftOption? SelectedShift { get => _selectedShift; set { if (!SetProperty(ref _selectedShift, value)) return; RefreshPlanOptions(); ClearPlanAndStdSelection(); } } private MesXslMixingProductionPlan? _selectedPlan; public MesXslMixingProductionPlan? SelectedPlan { get => _selectedPlan; set { if (!SetProperty(ref _selectedPlan, value)) return; ApplySelectedPlan(); } } private MesXslRubberQuickTestStd? _selectedStd; public MesXslRubberQuickTestStd? SelectedStd { get => _selectedStd; set { if (!SetProperty(ref _selectedStd, value)) return; _ = ApplySelectedStdAsync(); } } public string? MachineName => _selectedPlan?.MachineName ?? SelectedMachine; public string? WorkShiftDisplay => SelectedShift?.Name ?? string.Empty; public string? RubberMaterialName => _selectedPlan?.MaterialName ?? _selectedPlan?.FormulaName; private string? _testMethodName; public string? TestMethodName { get => _testMethodName; private set => SetProperty(ref _testMethodName, value); } private string? _trainNo; public string? TrainNo { get => _trainNo; set => SetProperty(ref _trainNo, value); } private string? _recordNo; /// 快检记录号(保存后由 MES 生成,只读) public string? RecordNo { get => _recordNo; private set => SetProperty(ref _recordNo, value); } private string _inspectorDisplay = string.Empty; /// 检验人(当前登录用户,只读) public string InspectorDisplay { get => _inspectorDisplay; private set => SetProperty(ref _inspectorDisplay, value); } private string _overallInspectResultDisplay = "不合格"; /// 检验结果(试验信息区汇总,只读) public string OverallInspectResultDisplay { get => _overallInspectResultDisplay; private set => SetProperty(ref _overallInspectResultDisplay, value); } /// 检验结果编码:1 合格,0 不合格 public string OverallInspectResultCode { get; private set; } = "0"; private string? _inspectTimesText; /// 试验次数(手填正整数) public string? InspectTimesText { get => _inspectTimesText; set => SetProperty(ref _inspectTimesText, value); } private double _upperMoldTemp; public double UpperMoldTemp { get => _upperMoldTemp; set => SetProperty(ref _upperMoldTemp, value); } private double _lowerMoldTemp; public double LowerMoldTemp { get => _lowerMoldTemp; set => SetProperty(ref _lowerMoldTemp, value); } private double _torqueS; public double TorqueS { get => _torqueS; set => SetProperty(ref _torqueS, value); } public DelegateCommand AddInspectRowCommand { get; } public DelegateCommand RemoveInspectRowCommand { get; } public DelegateCommand SaveCommand { get; } public DelegateCommand RefreshPlansCommand { get; } public DelegateCommand RefreshChartDemoCommand { get; } private async Task LoadLocalDataAsync(bool showSuccess) { IsLoading = true; try { _allPlans = await _planService.GetAllCachedAsync(); _allStds = await _stdService.GetAllCachedAsync(); RefreshMachineOptions(); if (showSuccess) Growl.Success("本地密炼计划与实验标准已刷新"); } catch (Exception ex) { Growl.Error($"加载本地数据失败:{ex.Message}"); } finally { IsLoading = false; } } private async Task ReloadLocalDataQuietAsync() { if (IsReadOnly) return; try { _allPlans = await _planService.GetAllCachedAsync(); _allStds = await _stdService.GetAllCachedAsync(); RefreshMachineOptions(); RefreshStdOptions(); } catch (Exception ex) { _logger.Warning($"[胶料快检记录] 本地数据刷新失败:{ex.Message}"); } } private IEnumerable FilteredByDate => _allPlans.Where(p => p.PlanDate?.Date == MixingDate.Date); private void RefreshMachineOptions() { MachineOptions.Clear(); foreach (var name in FilteredByDate.Select(o => o.MachineName).Where(n => !string.IsNullOrWhiteSpace(n)).Distinct().OrderBy(n => n)) MachineOptions.Add(name!); if (SelectedMachine != null && !MachineOptions.Contains(SelectedMachine)) SelectedMachine = null; if (SelectedMachine == null && MachineOptions.Count > 0) SelectedMachine = MachineOptions[0]; else RefreshShiftOptions(); } private void RefreshShiftOptions() { ShiftOptions.Clear(); var machine = SelectedMachine; var shifts = FilteredByDate .Where(o => string.IsNullOrWhiteSpace(machine) || o.MachineName == machine) .GroupBy(o => o.ShiftFlag?.ToString() ?? string.Empty) .Select(g => g.First()) .OrderBy(o => o.ShiftFlag); foreach (var s in shifts) ShiftOptions.Add(new WorkShiftOption(s.ShiftFlag?.ToString() ?? string.Empty, s.ShiftFlagText)); if (SelectedShift != null && !ShiftOptions.Any(x => x.Code == SelectedShift.Code)) SelectedShift = null; if (SelectedShift == null && ShiftOptions.Count > 0) SelectedShift = ShiftOptions[0]; else RefreshPlanOptions(); } private void RefreshPlanOptions() { PlanOptions.Clear(); var machine = SelectedMachine; var shiftCode = SelectedShift?.Code; foreach (var p in FilteredByDate .Where(x => string.IsNullOrWhiteSpace(machine) || x.MachineName == machine) .Where(x => string.IsNullOrWhiteSpace(shiftCode) || (x.ShiftFlag?.ToString() ?? string.Empty) == shiftCode) .Where(x => !string.IsNullOrWhiteSpace(x.PlanMaterialNo)) .OrderBy(x => x.PlanMaterialNo)) PlanOptions.Add(p); if (SelectedPlan != null && PlanOptions.All(p => p.Id != SelectedPlan.Id)) SelectedPlan = null; if (SelectedPlan == null && PlanOptions.Count > 0) SelectedPlan = PlanOptions[0]; } private void ClearPlanAndStdSelection() { _selectedPlan = null; RaisePropertyChanged(nameof(SelectedPlan)); RaisePropertyChanged(nameof(MachineName)); RaisePropertyChanged(nameof(WorkShiftDisplay)); RaisePropertyChanged(nameof(RubberMaterialName)); ClearStdSelection(); } private void ClearStdSelection() { _selectedStd = null; RaisePropertyChanged(nameof(SelectedStd)); TestMethodName = null; ClearStdAndResults(); StdOptions.Clear(); } private void ApplySelectedPlan() { RaisePropertyChanged(nameof(MachineName)); RaisePropertyChanged(nameof(WorkShiftDisplay)); RaisePropertyChanged(nameof(RubberMaterialName)); ClearStdSelection(); RefreshStdOptions(); } private void RefreshStdOptions() { StdOptions.Clear(); var rubberName = RubberMaterialName; if (string.IsNullOrWhiteSpace(rubberName)) return; foreach (var std in FilterApplicableStds(rubberName).OrderBy(s => s.StdName)) StdOptions.Add(std); if (StdOptions.Count == 0) Growl.Warning($"未找到胶料「{rubberName}」的使用中且已批准的快检实验标准,请先在「胶料快检实验标准」中同步数据"); RecalculateOverallInspectResult(); } private IEnumerable FilterApplicableStds(string rubberMaterialName) { var name = rubberMaterialName.Trim(); return _allStds.Where(std => string.Equals(std.RubberMaterialName?.Trim(), name, StringComparison.OrdinalIgnoreCase) && std.EnableStatus == "1" && std.AuditStatus == "1"); } private async Task ApplySelectedStdAsync() { DataPointColumns.Clear(); InspectRows.Clear(); _currentStd = null; TestMethodName = null; InspectColumnsChanged?.Invoke(); RecalculateOverallInspectResult(); if (SelectedStd == null || string.IsNullOrWhiteSpace(SelectedStd.Id)) return; IsLoading = true; try { _currentStd = await _stdService.GetWithLinesAsync(SelectedStd.Id); if (_currentStd == null) { Growl.Warning("所选实验标准在本地缓存中不存在,请刷新本地数据"); return; } TestMethodName = _currentStd.TestMethodName; if (_currentStd.LineList == null || _currentStd.LineList.Count == 0) { Growl.Warning($"标准「{_currentStd.StdName}」明细未同步到本地,请联网后重选,或先在「胶料快检实验标准」中查看该标准详情"); return; } foreach (var line in _currentStd.LineList.OrderBy(l => l.SortNo ?? 0)) DataPointColumns.Add(line); InspectColumnsChanged?.Invoke(); InitInspectRowsFromTimes(); AddInspectRowCommand.RaiseCanExecuteChanged(); } catch (Exception ex) { Growl.Error($"加载实验标准失败:{ex.Message}"); } finally { IsLoading = false; RecalculateOverallInspectResult(); } } private void ClearStdAndResults() { DataPointColumns.Clear(); InspectRows.Clear(); _currentStd = null; InspectColumnsChanged?.Invoke(); RecalculateOverallInspectResult(); } private void FillRandomChartData() { UpperTempValues.Clear(); LowerTempValues.Clear(); TorqueValues.Clear(); for (int i = 0; i < ChartPointCount; i++) { var time = ChartTimeMinutes[i]; var upper = 189 + _rnd.NextDouble() * 12; var lower = Math.Max(189, upper - 2 - _rnd.NextDouble() * 2); var torque = Math.Min(14.8, i * 2.5 + _rnd.NextDouble() * 2.5); UpperTempValues.Add(new ObservablePoint(time, Math.Round(upper, 2))); LowerTempValues.Add(new ObservablePoint(time, Math.Round(lower, 2))); TorqueValues.Add(new ObservablePoint(time, Math.Round(torque, 2))); } UpperMoldTemp = UpperTempValues[^1].Y ?? 0; LowerMoldTemp = LowerTempValues[^1].Y ?? 0; TorqueS = TorqueValues[^1].Y ?? 0; } private void InitInspectRowsFromTimes() { InspectRows.Clear(); var times = 1; if (TryParseInspectTimes(out var n, out _)) times = n; for (var i = 0; i < times; i++) AddInspectRow(); } private void RenumberInspectRows() { for (var i = 0; i < InspectRows.Count; i++) InspectRows[i].RowNo = $"{i + 1}_1"; } private void AddInspectRow() { var rowIndex = InspectRows.Count + 1; var row = new QuickTestInspectRowViewModel { RowNo = $"{rowIndex}_1" }; foreach (var col in DataPointColumns) { var cell = new QuickTestInspectCellViewModel { DataPointId = col.DataPointId, PointName = col.PointName ?? string.Empty, LowerLimit = col.LowerLimit, UpperLimit = col.UpperLimit }; cell.ValueChanged += _ => { row.RecalculateResult(); RecalculateOverallInspectResult(); }; row.Cells.Add(cell); } row.RecalculateResult(); InspectRows.Add(row); NotifySaveStateChanged(); RecalculateOverallInspectResult(); } private void RemoveInspectRow(QuickTestInspectRowViewModel? row) { if (row == null) return; InspectRows.Remove(row); SelectedInspectRow = null; RenumberInspectRows(); NotifySaveStateChanged(); RecalculateOverallInspectResult(); } private async Task SaveAsync() { if (_saveInProgress) return; _saveInProgress = true; NotifySaveStateChanged(); try { if (string.IsNullOrWhiteSpace(TrainNo)) { Growl.Warning("请填写车次"); return; } if (SelectedPlan == null) { Growl.Warning("请选择密炼计划"); return; } if (string.IsNullOrWhiteSpace(RubberMaterialName)) { Growl.Warning("请先选择密炼计划以带出胶料名称"); return; } if (SelectedStd == null || _currentStd == null) { Growl.Warning("请选择实验标准"); return; } if (InspectRows.Any(r => r.InspectResultText == "待填写" || string.IsNullOrWhiteSpace(r.InspectResultText))) { Growl.Warning("请手填全部检验行的检测值后再保存"); return; } if (!TryParseInspectTimes(out var inspectTimes, out var inspectTimesError)) { Growl.Warning(inspectTimesError); return; } var rawLineList = BuildRawLineList(); if (rawLineList.Count == 0) { Growl.Warning("无法生成试验结果原始数据"); return; } var stdLineList = BuildStdLineList(); if (stdLineList.Count == 0) { Growl.Warning("无法生成数据标准明细"); return; } var chartPointList = BuildChartPointList(); var user = AppSession.CurrentUser; IsLoading = true; var record = BuildSaveRecord(stdLineList, rawLineList, chartPointList, inspectTimes, user); var saved = await _recordService.SaveAsync(record); RecordNo = saved.Record.RecordNo; var syncHint = saved.SyncStatus == "Synced" ? "并已同步到 MES" : "(MES 同步待重试)"; Growl.Success(string.IsNullOrWhiteSpace(RecordNo) ? $"胶料快检记录已保存到本地{syncHint}" : $"胶料快检记录已保存,快检记录号:{RecordNo}{syncHint}"); InspectRows.Clear(); NotifySaveStateChanged(); } catch (Exception ex) { Growl.Error($"保存失败:{ex.Message}"); } finally { IsLoading = false; _saveInProgress = false; NotifySaveStateChanged(); } } private bool TryParseInspectTimes(out int value, out string error) { error = string.Empty; var text = string.IsNullOrWhiteSpace(InspectTimesText) ? "1" : InspectTimesText.Trim(); if (!int.TryParse(text, out var n) || n <= 0) { error = "试验次数须填写正整数"; value = 0; return false; } value = n; return true; } private List BuildAveragedLineList() { var lines = new List(); for (int i = 0; i < DataPointColumns.Count; i++) { var col = DataPointColumns[i]; var values = InspectRows .Where(r => i < r.Cells.Count && r.Cells[i].Value != null) .Select(r => r.Cells[i].Value!.Value) .ToList(); if (values.Count == 0) continue; var avg = values.Average(v => (double)v); var avgDecimal = Math.Round((decimal)avg, 6); var line = new MesXslRubberQuickTestRecordLine { SortNo = i }; if (!string.IsNullOrWhiteSpace(col.DataPointId)) line.DataPointId = col.DataPointId; if (!string.IsNullOrWhiteSpace(col.PointName)) line.InspectItem = col.PointName; if (col.LowerLimit != null) line.LowerLimit = col.LowerLimit; if (col.UpperLimit != null) line.UpperLimit = col.UpperLimit; line.InspectValue = avgDecimal; lines.Add(line); } return lines; } private List BuildRawLineList() { var rawLines = new List(); int sort = 0; foreach (var row in InspectRows) { for (int i = 0; i < row.Cells.Count; i++) { var cell = row.Cells[i]; if (cell.Value == null) continue; var raw = new MesXslRubberQuickTestRecordRawLine { SortNo = sort++ }; if (!string.IsNullOrWhiteSpace(row.RowNo)) raw.RowNo = row.RowNo; if (!string.IsNullOrWhiteSpace(cell.DataPointId)) raw.DataPointId = cell.DataPointId; if (!string.IsNullOrWhiteSpace(cell.PointName)) raw.InspectItem = cell.PointName; if (cell.LowerLimit != null) raw.LowerLimit = cell.LowerLimit; if (cell.UpperLimit != null) raw.UpperLimit = cell.UpperLimit; raw.InspectValue = cell.Value; raw.RowInspectResult = row.InspectResultCode; rawLines.Add(raw); } } return rawLines; } private bool HasExperimentStandardForRubber(string? rubberMaterialName) { if (string.IsNullOrWhiteSpace(rubberMaterialName)) return false; return FilterApplicableStds(rubberMaterialName).Any(); } private void RecalculateOverallInspectResult() { string display; string code; if (!HasExperimentStandardForRubber(RubberMaterialName) || _currentStd == null || DataPointColumns.Count == 0) { display = "不合格"; code = "0"; } else if (InspectRows.Count == 0 || InspectRows.Any(r => !string.Equals(r.InspectResultText, "合格", StringComparison.Ordinal))) { display = "不合格"; code = "0"; } else { display = "合格"; code = "1"; } OverallInspectResultCode = code; OverallInspectResultDisplay = display; } private MesXslRubberQuickTestRecord BuildSaveRecord( List stdLineList, List rawLineList, List chartPointList, int inspectTimes, SysUser? user) { var record = new MesXslRubberQuickTestRecord { StdLineList = stdLineList, RawLineList = rawLineList, ChartPointList = chartPointList, InspectTime = DateTime.Now, CreateTime = DateTime.Now, InspectResult = OverallInspectResultCode, RecordNo = _recordService.GenerateRecordNo(RubberMaterialName ?? string.Empty) }; if (!string.IsNullOrWhiteSpace(_currentStd?.RubberMaterialId)) record.RubberMaterialId = _currentStd.RubberMaterialId; if (!string.IsNullOrWhiteSpace(RubberMaterialName)) record.RubberMaterialName = RubberMaterialName; if (!string.IsNullOrWhiteSpace(_currentStd?.Id)) record.StdId = _currentStd.Id; if (!string.IsNullOrWhiteSpace(_currentStd?.StdName)) record.StdName = _currentStd.StdName; if (!string.IsNullOrWhiteSpace(_currentStd?.TestMethodId)) record.TestMethodId = _currentStd.TestMethodId; if (!string.IsNullOrWhiteSpace(TestMethodName)) record.TestMethodName = TestMethodName; if (!string.IsNullOrWhiteSpace(_currentStd?.QuickTestTypeId)) record.QuickTestTypeId = _currentStd.QuickTestTypeId; if (!string.IsNullOrWhiteSpace(_currentStd?.QuickTestTypeName)) record.QuickTestTypeName = _currentStd.QuickTestTypeName; if (!string.IsNullOrWhiteSpace(_selectedPlan?.MachineId)) record.ProdEquipmentLedgerId = _selectedPlan.MachineId; if (!string.IsNullOrWhiteSpace(MachineName)) record.ProdEquipmentName = MachineName; record.ProductionDate = MixingDate; if (!string.IsNullOrWhiteSpace(TrainNo)) record.TrainNo = TrainNo.Trim(); if (SelectedShift != null && !string.IsNullOrWhiteSpace(SelectedShift.Code)) record.WorkShift = SelectedShift.Code; record.InspectTimes = inspectTimes; if (!string.IsNullOrWhiteSpace(_selectedPlan?.PlanNo)) record.ProductionPlanNo = _selectedPlan.PlanNo; var inspectorId = user?.JeecgBizUserId ?? (user != null ? user.Id.ToString() : null); if (!string.IsNullOrWhiteSpace(inspectorId)) record.InspectorUserId = inspectorId; if (!string.IsNullOrWhiteSpace(user?.Account)) record.InspectorUsername = user.Account; if (!string.IsNullOrWhiteSpace(user?.RealName)) record.InspectorRealname = user.RealName; return record; } private List BuildStdLineList() { var lines = new List(); int sort = 0; foreach (var col in DataPointColumns) { lines.Add(new MesXslRubberQuickTestRecordStdLine { DataPointId = col.DataPointId, PointName = col.PointName, LowerLimit = col.LowerLimit, LowerWarn = col.LowerWarn, TargetValue = col.TargetValue, UpperWarn = col.UpperWarn, UpperLimit = col.UpperLimit, SortNo = sort++ }); } return lines; } private List BuildChartPointList() { var points = new List(); int sort = 0; for (int i = 0; i < ChartPointCount && i < UpperTempValues.Count; i++) { points.Add(new MesXslRubberQuickTestRecordChartPoint { TimeMin = (decimal)(UpperTempValues[i].X ?? ChartTimeMinutes[i]), UpperTemp = UpperTempValues[i].Y.HasValue ? (decimal)UpperTempValues[i].Y!.Value : null, LowerTemp = i < LowerTempValues.Count && LowerTempValues[i].Y.HasValue ? (decimal)LowerTempValues[i].Y!.Value : null, TorqueS = i < TorqueValues.Count && TorqueValues[i].Y.HasValue ? (decimal)TorqueValues[i].Y!.Value : null, SortNo = sort++ }); } return points; } private async Task LoadFromLocalItemAsync(string localId) { var item = _recordService.GetByLocalId(localId); if (item == null) { Growl.Warning("未找到快检记录"); return; } await ApplyRecordDetailAsync(item.Record); } private async Task LoadFromMesIdAsync(string mesId) { var record = await _recordService.GetByIdAsync(mesId); if (record == null) { Growl.Warning("未找到快检记录"); return; } await ApplyRecordDetailAsync(record); } private async Task ApplyRecordDetailAsync(MesXslRubberQuickTestRecord r) { IsReadOnly = true; RecordNo = r.RecordNo; MixingDate = r.ProductionDate ?? DateTime.Today; SelectedMachine = r.ProdEquipmentName; TrainNo = r.TrainNo; InspectTimesText = r.InspectTimes?.ToString() ?? "1"; InspectorDisplay = r.InspectorRealname ?? ResolveInspectorDisplay(); OverallInspectResultCode = r.InspectResult ?? "0"; OverallInspectResultDisplay = string.Equals(r.InspectResult, "1", StringComparison.Ordinal) ? "合格" : "不合格"; TestMethodName = r.TestMethodName; MachineOptions.Clear(); if (!string.IsNullOrWhiteSpace(r.ProdEquipmentName)) MachineOptions.Add(r.ProdEquipmentName); ShiftOptions.Clear(); var shift = new WorkShiftOption(r.WorkShift ?? "", r.WorkShift ?? ""); ShiftOptions.Add(shift); SelectedShift = shift; PlanOptions.Clear(); var plan = new MesXslMixingProductionPlan { PlanNo = r.ProductionPlanNo, MaterialName = r.RubberMaterialName, MachineName = r.ProdEquipmentName }; PlanOptions.Add(plan); _selectedPlan = plan; RaisePropertyChanged(nameof(SelectedPlan)); RaisePropertyChanged(nameof(MachineName)); RaisePropertyChanged(nameof(RubberMaterialName)); StdOptions.Clear(); var std = new MesXslRubberQuickTestStd { Id = r.StdId, StdName = r.StdName, TestMethodName = r.TestMethodName }; StdOptions.Add(std); _selectedStd = std; RaisePropertyChanged(nameof(SelectedStd)); DataPointColumns.Clear(); InspectRows.Clear(); foreach (var sl in (r.StdLineList ?? new List()).OrderBy(x => x.SortNo ?? 0)) { DataPointColumns.Add(new MesXslRubberQuickTestStdLine { DataPointId = sl.DataPointId, PointName = sl.PointName, LowerLimit = sl.LowerLimit, LowerWarn = sl.LowerWarn, TargetValue = sl.TargetValue, UpperWarn = sl.UpperWarn, UpperLimit = sl.UpperLimit, SortNo = sl.SortNo }); } InspectColumnsChanged?.Invoke(); var grouped = (r.RawLineList ?? new List()) .GroupBy(x => x.RowNo ?? string.Empty) .OrderBy(g => g.Key, StringComparer.Ordinal); foreach (var g in grouped) { var row = new QuickTestInspectRowViewModel { RowNo = g.Key }; for (int i = 0; i < DataPointColumns.Count; i++) { var col = DataPointColumns[i]; var raw = g.FirstOrDefault(x => x.DataPointId == col.DataPointId || x.InspectItem == col.PointName); var cell = new QuickTestInspectCellViewModel { DataPointId = col.DataPointId, PointName = col.PointName ?? string.Empty, LowerLimit = col.LowerLimit, UpperLimit = col.UpperLimit, Value = raw?.InspectValue }; row.Cells.Add(cell); } row.RecalculateResult(); InspectRows.Add(row); } UpperTempValues.Clear(); LowerTempValues.Clear(); TorqueValues.Clear(); foreach (var pt in (r.ChartPointList ?? new List()).OrderBy(x => x.SortNo ?? 0)) { var time = (double)(pt.TimeMin ?? 0); if (pt.UpperTemp != null) UpperTempValues.Add(new ObservablePoint(time, (double)pt.UpperTemp.Value)); if (pt.LowerTemp != null) LowerTempValues.Add(new ObservablePoint(time, (double)pt.LowerTemp.Value)); if (pt.TorqueS != null) TorqueValues.Add(new ObservablePoint(time, (double)pt.TorqueS.Value)); } if (UpperTempValues.Count > 0) UpperMoldTemp = UpperTempValues[^1].Y ?? 0; if (LowerTempValues.Count > 0) LowerMoldTemp = LowerTempValues[^1].Y ?? 0; if (TorqueValues.Count > 0) TorqueS = TorqueValues[^1].Y ?? 0; await Task.CompletedTask; } private static Axis[] BuildTimeAxis() => new[] { new Axis { Name = "时间(min)", MinLimit = 0, MaxLimit = 2, MinStep = 0.5, ForceStepToMin = true, LabelsPaint = new SolidColorPaint(SKColors.Gray), NamePaint = new SolidColorPaint(SKColors.Gray) } }; private static Axis[] BuildTempYAxis() => new[] { new Axis { Name = "温度(℃)", MinLimit = 189, MaxLimit = 201, MinStep = 3, ForceStepToMin = true, Labeler = v => FormatAxisTick(v, 189, 192, 195, 198, 201), LabelsPaint = new SolidColorPaint(SKColors.Gray), NamePaint = new SolidColorPaint(SKColors.Gray) } }; private static readonly double[] TorqueYTicks = { 0.0, 3.0, 5.9, 8.9, 11.8, 14.8 }; private static Axis[] BuildTorqueYAxis() => new[] { new Axis { Name = "S'(dNm)", MinLimit = 0, MaxLimit = 14.8, CustomSeparators = TorqueYTicks, Labeler = FormatTorqueAxisLabel, LabelsPaint = new SolidColorPaint(SKColors.Gray), NamePaint = new SolidColorPaint(SKColors.Gray) } }; private static string FormatTorqueAxisLabel(double value) { foreach (var tick in TorqueYTicks) { if (Math.Abs(value - tick) > 0.05) continue; return tick.ToString("0.0"); } return string.Empty; } private static string FormatAxisTick(double value, params double[] ticks) { foreach (var tick in ticks) { if (Math.Abs(value - tick) > 0.01) continue; return Math.Abs(tick - Math.Round(tick)) < 0.001 ? ((int)Math.Round(tick)).ToString() : tick.ToString("0.#"); } return string.Empty; } protected override void CleanUp() { if (_planChangedToken != null) { _eventAggregator.GetEvent().Unsubscribe(_planChangedToken); _planChangedToken = null; } if (_stdChangedToken != null) { _eventAggregator.GetEvent().Unsubscribe(_stdChangedToken); _stdChangedToken = null; } base.CleanUp(); } public new void Destroy() => base.Destroy(); } public class WorkShiftOption { public WorkShiftOption(string code, string name) { Code = code; Name = name; } public string Code { get; } public string Name { get; } }