Prechádzať zdrojové kódy

feat: 修改接收单

JaneDoe 2 mesiacov pred
rodič
commit
194ab837dd

+ 56 - 0
UniformMaterialManagementSystem/Utils/VisualUtil.cs

@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Media;
+using System.Windows;
+
+namespace UniformMaterialManagementSystem.Utils
+{
+    public static class VisualUtil
+    {
+        /// <summary>
+        /// 获取 VisualTree 中指定类型的子元素
+        /// </summary>
+        public static T? FindVisualChild<T>(DependencyObject obj) where T : DependencyObject // Visual 继承自 DependencyObject
+        {
+            T? child = default(T);
+            int num = VisualTreeHelper.GetChildrenCount(obj);
+            for (int i = 0; i < num; i++)
+            {
+                DependencyObject dObj = VisualTreeHelper.GetChild(obj, i);
+                child = dObj as T;
+                if (child != null)
+                {
+                    break;
+                }
+                else
+                {
+                    child = FindVisualChild<T>(dObj);
+                }
+            }
+            return child;
+        }
+
+        /// <summary>
+        /// 获取 VisualTree 中指定类型的父元素
+        /// </summary>
+        public static T? FindVisualParent<T>(DependencyObject obj) where T : DependencyObject
+        {
+            T? parent = default(T);
+            DependencyObject dObj = VisualTreeHelper.GetParent(obj);
+            parent = dObj as T;
+            if (parent != null)
+            {
+                return parent;
+            }
+            else
+            {
+                parent = FindVisualParent<T>(dObj);
+            }
+
+            return parent;
+        }
+    }
+}

+ 25 - 24
UniformMaterialManagementSystem/ViewModels/DeliveryReceiptViewModel.cs

@@ -494,12 +494,12 @@ namespace UniformMaterialManagementSystem.ViewModels
             {
                 // 判断已保存的行是否需要反写
                 SqliteContext? context = App.Current.Services.GetService<SqliteContext>();
-                if (context == null) { return; }
-                EntityEntry? entry = context.Entry(CurrDeliveryReceipt);
-                var modifiedProperties = entry.CurrentValues.Properties
-                                        .Where(p => entry.Property(p.Name).IsModified)
-                                        .Select(p => p.Name)
-                                        .ToList();
+                EntityEntry? entry = context?.Entry(CurrDeliveryReceipt);
+                if (entry == null) { return; }
+                List<string> modifiedProperties = entry.CurrentValues.Properties
+                                                  .Where(p => entry.Property(p.Name).IsModified)
+                                                  .Select(p => p.Name)
+                                                  .ToList();
                 needUpdtCD = modifiedProperties.Contains("ShippedQty") || modifiedProperties.Contains("ShippedPackets");
                 needUpdtIAD = modifiedProperties.Contains("ShippedPackets");
 
@@ -638,7 +638,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             CurrDeliveryReceipt = null;
 
             // 设置为已导出
-            foreach (var delivery in selectedDeliveries)
+            foreach (DeliveryReceipt delivery in selectedDeliveries)
             {
                 delivery.IsExportDelivery = true;
                 _service.Update(delivery);
@@ -659,7 +659,7 @@ namespace UniformMaterialManagementSystem.ViewModels
 
                 // 选择发货单的明细导出到 .db 文件
                 List<DeliveryReceiptDetail> details = new List<DeliveryReceiptDetail>();
-                foreach (var delivery in selectedDeliveries)
+                foreach (DeliveryReceipt delivery in selectedDeliveries)
                 {
                     details.AddRange(delivery.DeliveryReceiptDetails);
                 }
@@ -680,7 +680,7 @@ namespace UniformMaterialManagementSystem.ViewModels
 
             // 导出不成功,回滚导出状态
             if (!isNeedRollback) { return; }
-            foreach (var delivery in selectedDeliveries)
+            foreach (DeliveryReceipt delivery in selectedDeliveries)
             {
                 delivery.IsExportDelivery = false;
                 _service.Update(delivery);
@@ -716,7 +716,7 @@ namespace UniformMaterialManagementSystem.ViewModels
 
             // 新增发货单明细 - 方法一:
             List<DeliveryReceiptDetail> details = new List<DeliveryReceiptDetail>();
-            foreach (var d in deliveries)
+            foreach (DeliveryReceipt d in deliveries)
             {
                 details.AddRange(d.DeliveryReceiptDetails);
             }
@@ -1230,14 +1230,14 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
             // 清空子表数据
-            foreach (var dtl in CurrDeliveryReceipt.DeliveryReceiptDetails)
+            foreach (DeliveryReceiptDetail dtl in CurrDeliveryReceipt.DeliveryReceiptDetails)
             {
                 serviceDtl?.Delete(dtl);
             }
 
             // 导入的明细数据插入到数据库
             Dictionary<string, string> importPackBatchNos = new Dictionary<string, string>();
-            foreach (var dtl in importRows)
+            foreach (DeliveryReceiptDetail dtl in importRows)
             {
                 serviceDtl?.Insert(dtl);
 
@@ -1350,7 +1350,7 @@ namespace UniformMaterialManagementSystem.ViewModels
         /// <summary>
         /// 加载发货单数据
         /// </summary>
-        private void LoadDeliveryData()
+        internal void LoadDeliveryData()
         {
             // 筛选当前工作年度、当前生产企业的发货单
             var deliveryReceipts = _service.Query(x => x.WorkYear.Equals(_currUser!.WorkYear) && x.CompanyName.Equals(_currUser.CompanyName))
@@ -1364,7 +1364,7 @@ namespace UniformMaterialManagementSystem.ViewModels
                 delivery.IsNewRow = false;
 
                 // 子表明细行
-                var details = delivery.DeliveryReceiptDetails.OrderBy(x => x.PacketNo).ToArray();
+                var details = delivery.DeliveryReceiptDetails.OrderBy(x => x.PacketNo);
                 foreach (DeliveryReceiptDetail dtl in details)
                 {
                     dtl.IsNewRow = false;
@@ -1392,7 +1392,7 @@ namespace UniformMaterialManagementSystem.ViewModels
                 delivery.IsNewRow = false;
 
                 // 子表明细行
-                var details = delivery.DeliveryReceiptDetails.OrderBy(x => x.PacketNo).ToArray();
+                var details = delivery.DeliveryReceiptDetails.OrderBy(x => x.PacketNo);
                 foreach (DeliveryReceiptDetail dtl in details)
                 {
                     dtl.IsNewRow = false;
@@ -1417,7 +1417,7 @@ namespace UniformMaterialManagementSystem.ViewModels
                 delivery.IsNewRow = false;
 
                 // 子表明细行
-                var details = delivery.DeliveryReceiptDetails.OrderBy(x => x.PacketNo).ToArray();
+                var details = delivery.DeliveryReceiptDetails.OrderBy(x => x.PacketNo);
                 foreach (DeliveryReceiptDetail dtl in details)
                 {
                     dtl.IsNewRow = false;
@@ -1525,8 +1525,6 @@ namespace UniformMaterialManagementSystem.ViewModels
                 {
                     return true;
                 }
-
-                // todo 如果子表删除了几行是什么情况?
             }
 
             return false;
@@ -1589,7 +1587,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
             // 子表必须有行
-            var details = CurrDeliveryReceipt!.DeliveryReceiptDetails;
+            ObservableCollection<DeliveryReceiptDetail> details = CurrDeliveryReceipt!.DeliveryReceiptDetails;
             if (CurrDeliveryReceipt.ShippedPackets == 0 || CurrDeliveryReceipt.ShippedQty == 0 || details == null || details.Count == 0)
             {
                 MessageBox.Show("发运数量 或 发运包数为 0 ,不允许保存!");
@@ -1621,7 +1619,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
             // 检查发运数量列是否存在非数值型的值:防止输入异常数据后直接点保存按钮
-            foreach (var dtl in page.fdgDeliveryDetail.Items)
+            foreach (DeliveryReceiptDetail dtl in page.fdgDeliveryDetail.Items)
             {
                 DataGridRow? row = page.fdgDeliveryDetail.ItemContainerGenerator.ContainerFromItem(dtl) as DataGridRow;
                 if (row == null) { continue; }
@@ -1629,7 +1627,7 @@ namespace UniformMaterialManagementSystem.ViewModels
                 DataGridColumn? col = page.fdgDeliveryDetail.Columns.Where(x => x.Header.Equals("发运数量*")).FirstOrDefault() as DataGridColumn;
                 if (col == null) { continue; }
 
-                var cell = page.fdgDeliveryDetail.Columns[col.DisplayIndex].GetCellContent(row)?.Parent as DataGridCell;
+                DataGridCell? cell = page.fdgDeliveryDetail.Columns[col.DisplayIndex].GetCellContent(row)?.Parent as DataGridCell;
                 if (cell == null) { continue; }
                 string cellValue = "";
                 if (cell.Content is TextBox textBox)
@@ -1641,7 +1639,7 @@ namespace UniformMaterialManagementSystem.ViewModels
                     cellValue = textBlock.Text;
                 }
 
-                bool isValid = decimal.TryParse(cellValue, out var decimalValue);
+                bool isValid = decimal.TryParse(cellValue, out decimal decimalValue);
                 // bool hasError = Validation.GetHasError(cell); // 获取不到错误
                 if (!isValid)
                 {
@@ -1651,7 +1649,10 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
             // 保存时检查材料批号和包号不允许重复
-            var repeatNos = CurrDeliveryReceipt.DeliveryReceiptDetails.GroupBy(x => new { BatchNo = x.BatchNo, PacketNo = x.PacketNo }).Where(g => g.Count() > 1).Select(g => g.Key);
+            var repeatNos = CurrDeliveryReceipt.DeliveryReceiptDetails
+                                               .GroupBy(x => new { BatchNo = x.BatchNo, PacketNo = x.PacketNo })
+                                               .Where(g => g.Count() > 1)
+                                               .Select(g => g.Key);
             if (repeatNos.Any())
             {
                 // 若存在重复项则提示后返回
@@ -1671,7 +1672,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             StringBuilder batchNoMsg = new StringBuilder();
             StringBuilder packNoMsg = new StringBuilder();
             InspectApply? apply = null;
-            foreach (var dtl in CurrDeliveryReceipt.DeliveryReceiptDetails)
+            foreach (DeliveryReceiptDetail dtl in CurrDeliveryReceipt.DeliveryReceiptDetails)
             {
                 // 根据生产企业、产品名称、材料批号从数据库查询检验申请
                 string batchNo = dtl.BatchNo;

+ 4 - 26
UniformMaterialManagementSystem/ViewModels/MaterialCompanyViewModel.cs

@@ -20,6 +20,7 @@ using Microsoft.EntityFrameworkCore;
 using UniformMaterialManagementSystem.Entities;
 using UniformMaterialManagementSystem.Models;
 using UniformMaterialManagementSystem.Services;
+using UniformMaterialManagementSystem.Utils;
 using Company = UniformMaterialManagementSystem.Entities.Company;
 
 namespace UniformMaterialManagementSystem.ViewModels
@@ -370,29 +371,6 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
         }
 
-        /// <summary>
-        /// 获取 DataTemplate 的子元素
-        /// </summary>
-        private T? FindVisualChild<T>(DependencyObject obj) where T : DependencyObject // Visual 继承自 DependencyObject
-        {
-            T? child = default(T);
-            int num = VisualTreeHelper.GetChildrenCount(obj);
-            for (int i = 0; i < num; i++)
-            {
-                DependencyObject dObj = VisualTreeHelper.GetChild(obj, i);
-                child = dObj as T;
-                if (child != null)
-                {
-                    break;
-                }
-                else
-                {
-                    child = FindVisualChild<T>(dObj);
-                }
-            }
-            return child;
-        }
-
         /// <summary>
         /// 根据行列索引获取单元格
         /// </summary>
@@ -413,9 +391,9 @@ namespace UniformMaterialManagementSystem.ViewModels
             if (row == null) { return; }
 
             // 获取对应行上对应列的单元格
-            DataGridCellsPresenter? cellsPresenter = FindVisualChild<DataGridCellsPresenter>(row);
-            if (cellsPresenter == null) { return; }
-            DataGridCell cell = (DataGridCell)cellsPresenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
+            DataGridCellsPresenter? cellsPresenter = VisualUtil.FindVisualChild<DataGridCellsPresenter>(row);
+            DataGridCell? cell = cellsPresenter?.ItemContainerGenerator.ContainerFromIndex(columnIndex) as DataGridCell;
+            if (cell == null) { return; }
 
             // 正在编辑的单元格提交编辑
             dataGridMC.CommitEdit();

+ 198 - 109
UniformMaterialManagementSystem/ViewModels/MaterialReceiptViewModel.cs

@@ -8,8 +8,10 @@ using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
 using CommunityToolkit.Mvvm.ComponentModel;
 using CommunityToolkit.Mvvm.Input;
+using DocumentFormat.OpenXml.Wordprocessing;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.DependencyInjection;
 using UniformMaterialManagementSystem.Entities;
@@ -29,13 +31,13 @@ namespace UniformMaterialManagementSystem.ViewModels
         private ObservableCollection<DeliveryReceipt> _deliveryReceipts = [];
 
         [ObservableProperty]
-        private ObservableCollection<DeliveryReceiptDetail> _deliveryDetails = [];
+        private ObservableCollection<DeliveryReceiptDetail> _deliveryReceiptDetails = [];
 
         [ObservableProperty]
         private DeliveryReceipt? _currDeliveryReceipt = null;
 
-        // 删除时屏蔽行切换事件
-        private bool _isExcute = true;
+        // 行切换事件内部屏蔽自身
+        private bool _isExcuting = false;
 
         private string _exportFileName = "附件3成品生产企业报送接收数据模板";
 
@@ -61,41 +63,34 @@ namespace UniformMaterialManagementSystem.ViewModels
 
         public void FdgDelivery_SelectionChanged(object sender, SelectionChangedEventArgs args)
         {
-            // 手动屏蔽本事件
-            if (!_isExcute) { return; }
-            //args.Handled = true;
-
             if (sender is not DataGrid fdgDelivery) { return; }
 
-            // 暂时屏蔽行切换事件
-            _isExcute = false;
+            // 若本事件正在执行,则直接返回
+            if (_isExcuting) { return; }
+
+            // 暂时屏蔽本事件
+            _isExcuting = true;
 
             // 离开行之前,判断当前行是否未保存
             if (args.RemovedItems.Count > 0)
             {
-                // 手动切换为切换前的行(为了主表编辑界面的值不切换)
                 if (args.RemovedItems[0] is not DeliveryReceipt leavingDelivery) { return; }
-                fdgDelivery.SelectedItem = leavingDelivery;
-
-                // 判断当前行是否新增行或已修改
                 bool isChanged = IsChanged(leavingDelivery);
                 if (isChanged)
                 {
-                    // 询问是否切换
-                    MessageBoxResult res = MessageBox.Show("当前行已修改,切换行会丢失已修改的数据,是否确认切换行?", "询问", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
-                    // 不切换:重新赋值为切换前的行
+                    // 询问之前切换回离开前的行,防止下方编辑界面的值切换
+                    fdgDelivery.SelectedItem = leavingDelivery; // 会循环调用本事件,CurrDeliveryReceipt 双向绑定也会循环调用
+
+                    MessageBoxResult res = MessageBox.Show("当前行未保存,是否确认切换行?", "询问", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
                     if (res != MessageBoxResult.Yes)
                     {
-                        // 取消切换行:当前行不变
-                        fdgDelivery.SelectedItem = leavingDelivery; // 重新赋值为旧行; CurrDeliveryReceipt 通过绑定自动更新
-                        fdgDelivery.Items.Refresh(); // 必须刷新界面
-                        // 直接赋值会循环调用本事件,CurrDeliveryReceipt 双向绑定也会循环调用
+                        // 刷新界面:上方主表展示的行切换为旧行
+                        fdgDelivery.Items.Refresh();
 
-                        // 取消屏蔽后返回
-                        _isExcute = true;
+                        // 事件结束:不切换新行
+                        _isExcuting = false;
                         return;
                     }
-                    // 切换:则展示新行数据
                 }
             }
 
@@ -103,20 +98,21 @@ namespace UniformMaterialManagementSystem.ViewModels
             if (args.AddedItems.Count > 0)
             {
                 if (args.AddedItems[0] is not DeliveryReceipt newDelivery) { return; }
-                fdgDelivery.SelectedItem = newDelivery; // 刚刚为了界面数据赋值,所以这里需要设置为切换后的行
-                //fdgDelivery.Items.Refresh(); // 不必刷新前台界面
+                fdgDelivery.SelectedItem = newDelivery; // 自动更新DataGrid选中行和SelectedItem,但询问切换修改了值,需要手动切换回新行
+
+                // 更新子表的行
+                DeliveryReceiptDetails = new ObservableCollection<DeliveryReceiptDetail>(newDelivery.DeliveryReceiptDetails);
             }
 
-            // 取消屏蔽行切换
-            _isExcute = true;
+            // 事件结束
+            _isExcuting = false;
         }
 
         [RelayCommand]
-        public void Check(ComboBox cbReceivedStatus)
+        public void Check(DataGrid fdgDeliveryDetail)
         {
             if (CurrDeliveryReceipt == null) { return; }
 
-            var sele00 = cbReceivedStatus.SelectedItem;
             string status = CurrDeliveryReceipt.ReceivedStatus ?? string.Empty;
             if (status.Equals("已复核"))
             {
@@ -125,25 +121,11 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
             //  校验子表接收数量列
-            StringBuilder nullHint = new StringBuilder();
-            foreach (DeliveryReceiptDetail dtl in CurrDeliveryReceipt.DeliveryReceiptDetails)
-            {
-                decimal? receQty = dtl.ReceiveQuantity;
-                if (receQty == null || receQty == 0)
-                {
-                    int index = CurrDeliveryReceipt.DeliveryReceiptDetails.IndexOf(dtl);
-                    nullHint.AppendLine($"第 {index + 1} 行");
-                }
-            }
-            if (nullHint.Length > 0)
-            {
-                MessageBox.Show("子表中存在一下为空的接收数量,不允许复核!\n" + nullHint.ToString());
-                return;
-            }
+            CheckDetailDataGrid(fdgDeliveryDetail, true);
+            // MessageBox.Show("子表中存在以下为空的接收数量,不允许复核!\n" + nullHint.ToString());
 
             // 更新为已复核
             CurrDeliveryReceipt.ReceivedStatus = "已复核";
-            cbReceivedStatus.SelectedItem = "已复核";
         }
 
         // todo 
@@ -152,7 +134,7 @@ namespace UniformMaterialManagementSystem.ViewModels
         {
             // 未选择行或者当前行已导出,直接返回
             if (CurrDeliveryReceipt == null) { return; }
-            if (CurrDeliveryReceipt.IsExportDelivery || CurrDeliveryReceipt.IsExportReceive || CurrDeliveryReceipt.IsExportUsage) { return; }
+            if (CurrDeliveryReceipt.IsExportReceive) { return; }
 
             // 若 收货单状态 为空,默认赋值为 未复核
             var selectedStatus = cbReceivedStatus.SelectedItem;
@@ -162,13 +144,17 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
             // 子表存在空值时
-            var nullReceivedQty = CurrDeliveryReceipt!.DeliveryReceiptDetails.Where(x => x.ReceiveQuantity == 0);
+            var nullReceivedQty = DeliveryReceiptDetails.Where(x => x.ReceiveQuantity == 0);
             if (nullReceivedQty.Any())
             {
                 MessageBoxResult res = MessageBox.Show("明细表的接收数量存在空值,是否确认保存?", "询问", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
                 if (res != MessageBoxResult.Yes) { return; }
             }
 
+            // todo 检查每一行的数据:防止输入文本或超出发运数量时,不切换单元格(获取单元格 DataGridCell ,DataGridCell.Content 是TextBlock/TextBox,获取文本)
+
+
+
             // 保存到数据库
             var serviceDtl = App.Current.Services.GetService<IDataBaseService<DeliveryReceiptDetail>>();
             if (CurrDeliveryReceipt.IsNewRow)
@@ -177,10 +163,13 @@ namespace UniformMaterialManagementSystem.ViewModels
                 CurrDeliveryReceipt.IsNewRow = false;
                 _service.Insert(CurrDeliveryReceipt);
 
-                foreach (DeliveryReceiptDetail dtl in CurrDeliveryReceipt.DeliveryReceiptDetails)
+                foreach (DeliveryReceiptDetail dtl in DeliveryReceiptDetails)
                 {
-                    dtl.IsNewRow = false;
-                    serviceDtl?.Insert(dtl);
+                    //dtl.IsNewRow = false;
+                    //serviceDtl?.Insert(dtl);
+
+                    // 已有发货数据,所以不用 Insert() 用 Update() todo 要不要分表,如果要分表,需要判断 IsNewRow 后插入/更新
+                    serviceDtl?.Update(dtl);
                 }
             }
             else
@@ -189,17 +178,18 @@ namespace UniformMaterialManagementSystem.ViewModels
                 // todo 主表已保存,但子表明细 新增/删除/修改 怎么保存
                 _service.Update(CurrDeliveryReceipt);
 
-                foreach (DeliveryReceiptDetail dtl in CurrDeliveryReceipt.DeliveryReceiptDetails)
+                foreach (DeliveryReceiptDetail dtl in DeliveryReceiptDetails)
                 {
-                    if (dtl.IsNewRow)
-                    {
-                        dtl.IsNewRow = false;
-                        serviceDtl?.Insert(dtl);
-                    }
-                    else
-                    {
-                        serviceDtl?.Update(dtl);
-                    }
+                    serviceDtl?.Update(dtl);
+                    //if (dtl.IsNewRow)
+                    //{
+                    //    dtl.IsNewRow = false;
+                    //    serviceDtl?.Insert(dtl);
+                    //}
+                    //else
+                    //{
+                    //    serviceDtl?.Update(dtl);
+                    //}
                 }
             }
 
@@ -212,6 +202,8 @@ namespace UniformMaterialManagementSystem.ViewModels
                 MessageBox.Show("数据库保存失败!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                 return;
             }
+
+            MessageBox.Show("保存成功");
         }
 
         [RelayCommand]
@@ -223,6 +215,8 @@ namespace UniformMaterialManagementSystem.ViewModels
         [RelayCommand]
         public void ExportDB()
         {
+            // 成品企业-接收单导出附件3的时候赋值 IsExportReceive 字段为 true:导出后不可编辑,不可重复导出、
+
             if (CurrDeliveryReceipt != null)
             {
                 bool isChanged = IsChanged(CurrDeliveryReceipt);
@@ -300,20 +294,25 @@ namespace UniformMaterialManagementSystem.ViewModels
                 // 离开单元格时提交编辑
                 fdgDeliveryDetail.CommitEdit();
 
-                // 如果编辑的列是发运数量/接收数量/使用数量,自动汇总至主表 更新使用状态
+                // 如果编辑的列是 接收数量 ,并自动汇总至主表
                 foreach (DataGridCellInfo cellInfo in e.RemovedCells)
                 {
-                    // 删除行时为 null
-                    if (cellInfo.Column == null || cellInfo.Column.Header == null) { continue; } // DataGridCellInfo 类型的值永不等于 null
+                    // 忽略删除行导致的单元格切换
+                    if (!cellInfo.IsValid) { continue; }
 
                     if (!cellInfo.Column.Header.Equals("接收数量*")) { continue; }
 
+                    // todo 若输入的值不是 decimal,提示!
+
                     // 校验发运数量
                     DeliveryReceiptDetail? dtl = cellInfo.Item as DeliveryReceiptDetail;
                     if (dtl?.ReceiveQuantity < 0)
                     {
-                        dtl.ReceiveQuantity = 0;
-                        fdgDeliveryDetail.Items.Refresh();
+                        DeliveryReceiptDetail? dtl0 = DeliveryReceiptDetails[0];
+                        if (dtl0 == dtl) { } // true
+                        dtl.ReceiveQuantity = 0; // todo 使用 ObserveCollection[] 会不会自动刷新前台?
+                        fdgDeliveryDetail.Items.Refresh(); // 有效,但难免效率低一些
+                        //OnPropertyChanged(nameof(DeliveryReceiptDetails)); // 无效。
                         MessageBox.Show("输入的数量异常,请重新输入!");
                         return;
                     }
@@ -322,6 +321,7 @@ namespace UniformMaterialManagementSystem.ViewModels
                         dtl.ReceiveQuantity = dtl?.ShippedQuantity;
                         // todo 效率低,更高效的写法?
                         fdgDeliveryDetail.Items.Refresh();
+                        // fdgDeliveryDetail.CommitEdit(); // 不能更新到前台
                         //OnPropertyChanged(nameof(dtl)); // 无效
                         //OnPropertyChanged(nameof(CurrDeliveryReceipt.DeliveryReceiptDetails));
                         MessageBox.Show("接收数量不能超过发运数量,请重新输入");
@@ -329,15 +329,15 @@ namespace UniformMaterialManagementSystem.ViewModels
                     }
 
                     // 汇总主表接收数量
-                    var validDtls = CurrDeliveryReceipt!.DeliveryReceiptDetails.Where(x => x.ReceiveQuantity != null && x.ReceiveQuantity > 0);
-                    CurrDeliveryReceipt.ReceivedPackets = validDtls.Count();
+                    var validDtls = DeliveryReceiptDetails.Where(x => x.ReceiveQuantity != null && x.ReceiveQuantity > 0);
+                    CurrDeliveryReceipt!.ReceivedPackets = validDtls.Count();
                     CurrDeliveryReceipt.ReceivedQty = validDtls.Select(x => x.ReceiveQuantity).Sum() ?? 0;
                 }
 
                 // 手动触发值更新事件:此时没有切换焦点,不会自动触发更新
                 OnPropertyChanged(nameof(CurrDeliveryReceipt));
                 //OnPropertyChanged(nameof(CurrDeliveryReceipt.DeliveryReceiptDetails));
-                fdgDeliveryDetail.Items.Refresh();
+                //fdgDeliveryDetail.Items.Refresh();
             }
 
             // 单元格切换时,Added 里的是新单元格;Removed是旧单元格
@@ -348,19 +348,12 @@ namespace UniformMaterialManagementSystem.ViewModels
                     // 进入单元格时自动进入编辑状态
                     fdgDeliveryDetail.BeginEdit();
 
-                    // todo IsValid ?
-                    if (cellInfo.IsValid) { }
-                    if (cellInfo.Column == null)
-                    {
-                        continue;
-                    }
-
-                    // todo 点击两下才能勾选/取消勾选
                     // 勾选/取消勾选行
                     if (cellInfo.Column is DataGridCheckBoxColumn colCheck)
                     {
                         if (cellInfo.Item is not DeliveryReceiptDetail dtl) { continue; }
                         dtl.IsSelected = !dtl.IsSelected;
+                        fdgDeliveryDetail.CommitEdit(); // 前台刷新勾选状态
                     }
                 }
             }
@@ -370,40 +363,86 @@ namespace UniformMaterialManagementSystem.ViewModels
         public void ReceiveAll(MaterialReceiptPage page)
         {
             if (CurrDeliveryReceipt == null) { return; }
+            if (DeliveryReceiptDetails == null || DeliveryReceiptDetails.Count == 0) { return; }
 
-            ObservableCollection<DeliveryReceiptDetail> details = CurrDeliveryReceipt.DeliveryReceiptDetails;
-            if (details == null || details.Count == 0) { return; }
+            // 提交编辑:
+            page.fdgDeliveryDetail.CommitEdit();
+            //page.fdgDeliveryDetail.UnselectAllCells(); // 会触发单元格切换,这里不用校验发货数量,直接更新就好
 
-            // 全部接收:将发货数量列赋值到接收数量列
-            decimal qtyAll = 0;
-            foreach (DeliveryReceiptDetail dtl in details)
+            // 全部接收:将子表发货数量列赋值到接收数量列
+            var serviceDtl = App.Current.Services.GetService<IDataBaseService<DeliveryReceiptDetail>>();
+            foreach (DeliveryReceiptDetail dtl in DeliveryReceiptDetails)
             {
-                dtl.ReceiveQuantity = dtl.ShippedQuantity;
-                qtyAll += dtl.ShippedQuantity;
+                if (dtl.ReceiveQuantity == dtl.ShippedQuantity) { continue; } // 若录入值=发货数量,不变
+
+                dtl.ReceiveQuantity = dtl.ShippedQuantity; // 如果没录入值,更新为发货数量;若录入值,但<发货数量,更新为发货数量;
+                                                           // 录入值>发货数量的情况不会存在,在切换单元格/保存时会校验
+                serviceDtl?.Update(dtl);
+                //if (dtl.IsNewRow)
+                //{
+                //    dtl.IsNewRow = false;
+                //    serviceDtl?.Insert(dtl);
+                //}
+                //else
+                //{
+                //    serviceDtl?.Update(dtl);
+                //}
             }
 
             // 汇总到主表
-            CurrDeliveryReceipt.ReceivedPackets = details.Count();
+            int count = DeliveryReceiptDetails.Where(x => x.ReceiveQuantity > 0).Count();
+            CurrDeliveryReceipt.ReceivedPackets = count;
+            decimal qtyAll = DeliveryReceiptDetails.Select(x => x.ReceiveQuantity).Sum() ?? 0;
             CurrDeliveryReceipt.ReceivedQty = qtyAll;
-            OnPropertyChanged(nameof(CurrDeliveryReceipt));
-            OnPropertyChanged(nameof(CurrDeliveryReceipt.DeliveryReceiptDetails));
 
-            // 保存到数据库
-            Save(page.cbReceivedStatus);
+            // 若 收货单状态 为空,默认赋值为 未复核
+            string? status = CurrDeliveryReceipt.ReceivedStatus;
+            if (status == null || string.IsNullOrEmpty(status))
+            {
+                CurrDeliveryReceipt.ReceivedStatus = "未复核";
+            }
 
+            if (CurrDeliveryReceipt.IsNewRow)
+            {
+                CurrDeliveryReceipt.IsNewRow = false;
+                _service.Insert(CurrDeliveryReceipt);
+            }
+            else
+            {
+                _service.Update(CurrDeliveryReceipt);
+            }
+
+            bool isSuccessMain = _service.SaveChanges(); // DataContext.SaveChanges() 会自动更新所有与当前上下文关联的实体的更改,包括关联表的更改。
+                                                         // EFCore 通过 DbContext 跟踪所有被加载到上下文中的实体。当你对这些实体进行修改、添加或删除操作时,EFCore 会记录这些更改。
+                                                         // 如果你修改了某个实体的导航属性(例如,一对多关系中的集合属性),EFCore 会自动跟踪这些更改,并在调用 SaveChanges() 时将这些更改保存到数据库中。
+                                                         // 在这种情况下,EFCore 会自动将新的 Book 实体保存到数据库中,并更新 Author 实体的关联表。
+            if (!isSuccessMain)
+            {
+                MessageBox.Show("更新到数据库失败,请联系系统管理员!");
+                return;
+            }
+
+            // 检查子表有没有更新到数据库
+            var savedDtls = serviceDtl?.Query(x => x.DeliveryReceiptGuid.Equals(CurrDeliveryReceipt.Guid));
+            foreach (DeliveryReceiptDetail sDtl in savedDtls)
+            {
+
+            }
+
+            // 更新到前台界面?
+            OnPropertyChanged(nameof(CurrDeliveryReceipt));
+            //OnPropertyChanged(nameof(CurrDeliveryReceipt.DeliveryReceiptDetails));
             // 刷新子表数据:值已更新,但没有显示
+            page.fdgDeliveryDetail.CommitEdit(); // 当正在编辑单元格时,刷新会报错(单元格在编辑状态时直接点击 全部接收 按钮会报错)
             page.fdgDeliveryDetail.Items.Refresh();
         }
 
         /// <summary>
         /// 加载接收单数据
         /// </summary>
-        private void LoadReceiptData()
+        internal void LoadReceiptData()
         {
-            // 材料企业-发货单导出数据包赋值 IsExportDelivery 字段为 true:导出后不可再修改、删除
-            // 成品企业-接收单导出附件3的时候赋值 IsExportReceive 字段为 true:导出后不可编辑,不可重复导出
-            // 成品企业-使用单导出附件4的时候赋值 IsExportUsage 字段为 true:导出后不可编辑,不可重复导出
-            // 筛选当前工作年度、当前成品企业的所有发货单(数据包导入)
+            // 筛选当前工作年度、当前成品企业的所有发货单
             var deliveryReceipts = _service.Query(x => x.WorkYear.Equals(_currUser!.WorkYear) && x.ReceivedCompanyName.Equals(_currUser.CompanyName))
                                            .Include(x => x.DeliveryReceiptDetails);
             DeliveryReceipts = new ObservableCollection<DeliveryReceipt>(deliveryReceipts);
@@ -413,16 +452,14 @@ namespace UniformMaterialManagementSystem.ViewModels
                 // 是否为新行:否
                 delivery.IsNewRow = false;
 
-                // 收货单状态
-                var status = delivery.ReceivedStatus;
-
-                // 子表明细行
-                var details = delivery.DeliveryReceiptDetails.OrderBy(x => x.PacketNo).ToArray();
-                foreach (DeliveryReceiptDetail dtl in details)
-                {
-                    dtl.IsNewRow = false;
-                }
-                delivery.DeliveryReceiptDetails = new ObservableCollection<DeliveryReceiptDetail>(details);
+                // todo 不分表不用判断,如果分表,需要判断
+                // 子表明细行:子表的 IsNewRow 用于保存子表是判断 Insert() / Update() 
+                //var details = delivery.DeliveryReceiptDetails.OrderBy(x => x.PacketNo);
+                //foreach (DeliveryReceiptDetail dtl in details)
+                //{
+                //    dtl.IsNewRow = false;
+                //}
+                //delivery.DeliveryReceiptDetails = new ObservableCollection<DeliveryReceiptDetail>(details);
             }
         }
 
@@ -452,24 +489,76 @@ namespace UniformMaterialManagementSystem.ViewModels
                     return true;
                 }
 
+                // 会自动提交子表的编辑,不用手动 CommitEdit()
                 EntityState? detailState = App.Current.Services.GetService<IDataBaseService<DeliveryReceiptDetail>>()?.Entry(detail);
                 if (detailState != EntityState.Unchanged)
                 {
                     return true;
                 }
-
-                // todo 如果子表删除了几行是什么情况?
             }
 
             return false;
         }
 
-        /// <summary>
-        /// 汇总接收数据
-        /// </summary>
-        private void SummaryReceiveDetail()
+        private bool CheckDetailDataGrid(DataGrid fdgDeliveryDetail, bool isCheckZero)
         {
+            foreach (DeliveryReceiptDetail dtl in fdgDeliveryDetail.Items)
+            {
+                // 获取行
+                DataGridRow? row = fdgDeliveryDetail.ItemContainerGenerator.ContainerFromItem(dtl) as DataGridRow;
+                if (row == null)
+                {
+                    // 获取失败则滚动界面后重试
+                    fdgDeliveryDetail.UpdateLayout();
+                    fdgDeliveryDetail.ScrollIntoView(dtl);
+                    row = fdgDeliveryDetail.ItemContainerGenerator.ContainerFromItem(dtl) as DataGridRow;
+                }
+                if (row == null) { continue; }
+
+                // 获取发运数量单元格
+                DataGridCellsPresenter? cellsPresenter = VisualUtil.FindVisualChild<DataGridCellsPresenter>(row);
+                DataGridCell? cell = cellsPresenter?.ItemContainerGenerator.ContainerFromIndex(3) as DataGridCell; // 不允许拖动改变列顺序
+                if (cell == null) { continue; }
+
+                // 
+                string? cellValue = "";
+                if (cell.Content is TextBox textBox)
+                {
+                    cellValue = textBox.Text;
+                }
+                else if (cell.Content is TextBlock textBlock)
+                {
+                    cellValue = textBlock.Text;
+                }
+
+                // 非数值型:包括空?
+                bool isValid = decimal.TryParse(cellValue, out decimal receiveQty);
+                if (!isValid)
+                {
+                    MessageBox.Show("子表[接受数量]列存在异常数据,请检查数据后重试!");
+                    return false;
+                }
+                // 负数
+                if (receiveQty < 0)
+                {
+                    MessageBox.Show("子表[接受数量]列存在异常数据,请检查数据后重试!");
+                    return false;
+                }
+                // 0
+                if (isCheckZero && receiveQty == 0)
+                {
+                    MessageBox.Show("子表[接受数量]列存在为 0 的数据,请检查数据后重试!");
+                    return false;
+                }
+                // 超出发运数量
+                if (receiveQty > dtl.ShippedQuantity)
+                {
+                    MessageBox.Show("子表[接受数量]列存在为超出[发运数量]的数据,请检查数据后重试!");
+                    return false;
+                }
+            }
 
+            return true;
         }
     }
 }

+ 9 - 32
UniformMaterialManagementSystem/ViewModels/MaterialViewModel.cs

@@ -207,7 +207,7 @@ namespace UniformMaterialManagementSystem.ViewModels
 
             // 新增行插入数据库
             int changedCount = 0;
-            foreach (var item in Materials)
+            foreach (Material item in Materials)
             {
                 // 跳过已保存行
                 if (!item.IsNewRow) { continue; }
@@ -259,7 +259,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             // todo OpenXml 读取时会自动跳过空白单元格(因为Excel的xml只存储有数据的单元格的内容),所以当行存在空白单元格时,会自动左移
             DataTable dtImport0 = ExcelUtil.ReadExcelFileToDataTable(openFileDialog.FileName);
             DataTable? dtImport = OpenXmlExcelUtil.ExcelToDataTable(openFileDialog.FileName);
-            if(dtImport == null || dtImport.Rows.Count == 0)
+            if (dtImport == null || dtImport.Rows.Count == 0)
             {
                 MessageBox.Show("获取Excel数据失败,请重试!");
                 return;
@@ -267,7 +267,7 @@ namespace UniformMaterialManagementSystem.ViewModels
 
             // 检查 Excel 中是否存在重复项
             StringBuilder checkExcelMsg = new StringBuilder();
-            var importRepeatNames = dtImport.AsEnumerable().GroupBy(x => x["材料名称*"]).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
+            List<string> importRepeatNames = dtImport.AsEnumerable().GroupBy(x => x["材料名称*"].ToString() ?? "").Where(g => g.Count() > 1).Select(g => g.Key).ToList();
             if (importRepeatNames.Any())
             {
                 string names = string.Join("、", importRepeatNames);
@@ -321,7 +321,7 @@ namespace UniformMaterialManagementSystem.ViewModels
                 importNullMsg = importNullMsg.Remove(importNullMsg.Length - 1, 1);
                 importNullMsg = importNullMsg.Insert(0, "忽略存在为空的必录项的行:").AppendLine();
             }
-            
+
 
             if (importCount > 0)
             {
@@ -384,7 +384,7 @@ namespace UniformMaterialManagementSystem.ViewModels
 
             // 绑定到界面
             Materials.Clear();
-            foreach (var mate in materials)
+            foreach (Material mate in materials)
             {
                 mate.IsNewRow = false;// 已保存的行 IsNewRow 为 false
                 Materials.Add(mate);
@@ -407,29 +407,6 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
         }
 
-        /// <summary>
-        /// 获取 DataTemplate 的子元素
-        /// </summary>
-        private T? FindVisualChild<T>(DependencyObject obj) where T : DependencyObject // Visual 继承自 DependencyObject
-        {
-            T? child = default(T);
-            int num = VisualTreeHelper.GetChildrenCount(obj);
-            for (int i = 0; i < num; i++)
-            {
-                DependencyObject dObj = VisualTreeHelper.GetChild(obj, i);
-                child = dObj as T;
-                if (child != null)
-                {
-                    break;
-                }
-                else
-                {
-                    child = FindVisualChild<T>(dObj);
-                }
-            }
-            return child;
-        }
-
         /// <summary>
         /// 根据行列索引获取单元格
         /// </summary>
@@ -450,9 +427,9 @@ namespace UniformMaterialManagementSystem.ViewModels
             if (row == null) { return; }
 
             // 获取对应行上对应列的单元格
-            DataGridCellsPresenter? cellsPresenter = FindVisualChild<DataGridCellsPresenter>(row);
-            if (cellsPresenter == null) { return; }
-            DataGridCell cell = (DataGridCell)cellsPresenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
+            DataGridCellsPresenter? cellsPresenter = VisualUtil.FindVisualChild<DataGridCellsPresenter>(row);
+            DataGridCell? cell = cellsPresenter?.ItemContainerGenerator.ContainerFromIndex(columnIndex) as DataGridCell;
+            if (cell == null) { return; }
 
             // 正在编辑的单元格提交编辑
             dataGrid.CommitEdit();
@@ -483,7 +460,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             dtMate.Columns.Add("是否可用", typeof(string));
 
             // 添加数据
-            foreach (var mate in Materials)
+            foreach (Material mate in Materials)
             {
                 DataRow dr = dtMate.NewRow();
                 dr["材料名称*"] = mate.Name;

+ 19 - 40
UniformMaterialManagementSystem/ViewModels/SelectApplyContractDialogViewModel.cs

@@ -17,6 +17,7 @@ using UniformMaterialManagementSystem.Custom;
 using UniformMaterialManagementSystem.Entities;
 using UniformMaterialManagementSystem.Models;
 using UniformMaterialManagementSystem.Services;
+using UniformMaterialManagementSystem.Utils;
 using Contract = UniformMaterialManagementSystem.Entities.Contract;
 
 namespace UniformMaterialManagementSystem.ViewModels
@@ -86,7 +87,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             double rowHeaderWidth1 = fdgContractDetail.RowHeaderWidth; // 30
             double rowHeaderWidth2 = fdgContractDetail.RowHeaderActualWidth; // 30
             double avgColWidth0 = -1;
-            ScrollViewer? scrollViewer = FindVisualChild<ScrollViewer>(fdgContractDetail);
+            ScrollViewer? scrollViewer = VisualUtil.FindVisualChild<ScrollViewer>(fdgContractDetail);
             if (scrollViewer != null)
             {
                 double width0 = scrollViewer.Width; // NaN
@@ -94,8 +95,8 @@ namespace UniformMaterialManagementSystem.ViewModels
                 //double width2 = scrollViewer.ViewportWidth; // 1407 // SizeChanged 后 ViewportWidth 比 ActualWidth 少了22,ViewportWidth 更小了
                 //double width1 = scrollViewer.ScrollInfo.ActualWidth; // 访问限制
 
-                var scrollBar0 = FindVisualChild<ScrollBar>(fdgContractDetail); // null
-                var scrollBar = FindVisualChild<ScrollBar>(scrollViewer); // 能获取到
+                var scrollBar0 = VisualUtil.FindVisualChild<ScrollBar>(fdgContractDetail); // null
+                var scrollBar = VisualUtil.FindVisualChild<ScrollBar>(scrollViewer); // 能获取到
                 if (scrollBar == null) { return; }
 
                 double barWidth0 = scrollBar.ActualWidth; // 0
@@ -107,7 +108,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
 
-            foreach (var col in fdgContractDetail.Columns)
+            foreach (DataGridColumn col in fdgContractDetail.Columns)
             {
                 if (col.Header.Equals("材料名称"))
                 {
@@ -132,15 +133,15 @@ namespace UniformMaterialManagementSystem.ViewModels
             double rowHeaderWidth1 = fdgContractDetail.RowHeaderWidth; // 30
             double rowHeaderWidth2 = fdgContractDetail.RowHeaderActualWidth; // 30
             double avgColWidth0 = -1;
-            ScrollViewer? scrollViewer = FindVisualChild<ScrollViewer>(fdgContractDetail);
+            ScrollViewer? scrollViewer = VisualUtil.FindVisualChild<ScrollViewer>(fdgContractDetail);
             if (scrollViewer != null)
             {
                 double width0 = scrollViewer.Width; // NaN
                 double width1 = scrollViewer.ActualWidth; // 1422
                 double width2 = scrollViewer.ViewportWidth; // 1407
 
-                var scrollBar0 = FindVisualChild<ScrollBar>(fdgContractDetail); // null
-                var scrollBar = FindVisualChild<ScrollBar>(scrollViewer); // 能获取到
+                var scrollBar0 = VisualUtil.FindVisualChild<ScrollBar>(fdgContractDetail); // null
+                var scrollBar = VisualUtil.FindVisualChild<ScrollBar>(scrollViewer); // 能获取到
                 if (scrollBar == null) { return; }
 
                 double barWidth0 = scrollBar.ActualWidth; // 0
@@ -152,7 +153,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
 
-            foreach (var col in fdgContractDetail.Columns)
+            foreach (DataGridColumn col in fdgContractDetail.Columns)
             {
                 if (col.Header.Equals("材料名称"))
                 {
@@ -176,7 +177,7 @@ namespace UniformMaterialManagementSystem.ViewModels
         {
             // 获取当前用户的数据
             int workYear = _currUser!.WorkYear;
-            var currCompanyGuid = _currUser.CompanyGuid;
+            Guid? currCompanyGuid = _currUser.CompanyGuid;
             if (currCompanyGuid == null)
             {
                 MessageBox.Show("没有获取到当前用户的生产企业数据,请联系系统管理员!");
@@ -201,10 +202,11 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
             // 获取主表数据源:前台绑定需要 Include() 两个企业字段
-            var contracts = App.Current.Services.GetService<IDataBaseService<Contract>>()?.Query(x => x.CompanyGuid.Equals(currCompanyGuid) && x.Year.Equals(workYear))
-                .Include(x => x.Company)
-                .Include(x => x.PurchaseCompany)
-                .Include(x => x.ContractDetails);
+            var contracts = App.Current.Services.GetService<IDataBaseService<Contract>>()?
+                                                .Query(x => x.CompanyGuid.Equals(currCompanyGuid) && x.Year.Equals(workYear))
+                                                .Include(x => x.Company)
+                                                .Include(x => x.PurchaseCompany)
+                                                .Include(x => x.ContractDetails);
             if (contracts == null || !contracts.Any())
             {
                 MessageBox.Show("获取合同数据失败!");
@@ -212,11 +214,11 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
             // 根据合同明细筛选合同
-            List<Contract>? mateContracts = new List<Contract>();
-            foreach (var contract in contracts)
+            List<Contract> mateContracts = new List<Contract>();
+            foreach (Contract contract in contracts)
             {
-                var details = contract.ContractDetails;
-                var count = details.Where(x => x.MaterialGuid.Equals(SelectedMaterial.Guid) && !x.ShippedStatus).Count();
+                var count = contract.ContractDetails.Where(x => x.MaterialGuid.Equals(SelectedMaterial.Guid) && !x.ShippedStatus)
+                                                    .Count();
                 if (count > 0)
                 {
                     mateContracts.Add(contract);
@@ -306,28 +308,5 @@ namespace UniformMaterialManagementSystem.ViewModels
 
             window.Close();
         }
-
-        /// <summary>
-        /// 获取 VisualTree 指定类型的子元素
-        /// </summary>
-        private T? FindVisualChild<T>(DependencyObject obj) where T : DependencyObject // Visual 继承自 DependencyObject
-        {
-            T? child = default(T);
-            int num = VisualTreeHelper.GetChildrenCount(obj);
-            for (int i = 0; i < num; i++)
-            {
-                DependencyObject dObj = VisualTreeHelper.GetChild(obj, i);
-                child = dObj as T;
-                if (child != null)
-                {
-                    break;
-                }
-                else
-                {
-                    child = FindVisualChild<T>(dObj);
-                }
-            }
-            return child;
-        }
     }
 }

+ 9 - 56
UniformMaterialManagementSystem/ViewModels/UsersViewModel.cs

@@ -183,9 +183,9 @@ namespace UniformMaterialManagementSystem.ViewModels
 
                 // 根据 ComboBox 选择项获取内容控件
                 // 方法一:可以获取到准确的子控件
-                CheckBox? checkBox = FindVisualChild<CheckBox>(comboBoxItem);
-                TextBlock? textBlock = FindVisualChild<TextBlock>(comboBoxItem);
-                var text = textBlock?.Text;
+                CheckBox? checkBox = VisualUtil.FindVisualChild<CheckBox>(comboBoxItem);
+                TextBlock? textBlock = VisualUtil.FindVisualChild<TextBlock>(comboBoxItem);
+                string? text = textBlock?.Text;
 
                 // 方法二:可以获取到准确的子控件
                 //ContentPresenter? contentPresenter = FindVisualChild<ContentPresenter>(comboBoxItem);
@@ -223,8 +223,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             // 多选 ComboBox 的显示文本在 Load 事件中设置,每次点击之后重新根据当前角色设置(DisplayMemberPath 和 ItemTemplate 冲突,只设置其中一个)
 
             // 单元格自动提交编辑状态
-            DataGrid? dataGrid = FindVisualParent<DataGrid>(cbMultiRole!);
-            if (dataGrid == null) { return; }
+            DataGrid? dataGrid = VisualUtil.FindVisualParent<DataGrid>(cbMultiRole!);
             dataGrid?.CommitEdit(); // 提交编辑:退出编辑状态并选中当前单元格
             // todo 单元格退出编辑?
             //dataGrid?.UnselectAllCells(); // 不提交编辑,直接取消选中,至少会不显示拼接后的文本,有可能会丢失获取的值
@@ -462,7 +461,7 @@ namespace UniformMaterialManagementSystem.ViewModels
                 StringBuilder illegalMsg = new StringBuilder();
                 Dictionary<int, int> illegalIndexes = new Dictionary<int, int>();
                 int firstIllegalRowIndex = UserModels.Count;
-                foreach (var illegalRow in illegalRows)
+                foreach (UserModel illegalRow in illegalRows)
                 {
                     int rowIndex = UserModels.IndexOf(illegalRow);
                     illegalMsg.Append($"\n第 {rowIndex} 行: '{illegalRow.LoginName}'");
@@ -531,7 +530,7 @@ namespace UniformMaterialManagementSystem.ViewModels
 
             // 新增行插入数据库
             int changedCount = 0;
-            foreach (var useModel in UserModels)
+            foreach (UserModel useModel in UserModels)
             {
                 // 跳过已保存行
                 if (!useModel.IsNewRow) { continue; }
@@ -654,52 +653,6 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
         }
 
-        /// <summary>
-        /// 获取 DataTemplate 的子元素
-        /// </summary>
-        private T? FindVisualChild<T>(DependencyObject obj) where T : DependencyObject // Visual 继承自 DependencyObject
-        {
-            T? child = default(T);
-            int num = VisualTreeHelper.GetChildrenCount(obj);
-            for (int i = 0; i < num; i++)
-            {
-                DependencyObject dObj = VisualTreeHelper.GetChild(obj, i);
-                child = dObj as T;
-                if (child != null)
-                {
-                    break;
-                }
-                else
-                {
-                    child = FindVisualChild<T>(dObj);
-                }
-            }
-            return child;
-        }
-
-        /// <summary>
-        /// 利用VisualTreeHelper寻找指定依赖对象的父级对象
-        /// </summary>
-        /// <typeparam name="T"></typeparam>
-        /// <param name="obj"></param>
-        /// <returns></returns>
-        private T? FindVisualParent<T>(DependencyObject obj) where T : DependencyObject
-        {
-            T? parent = default(T);
-            DependencyObject dObj = VisualTreeHelper.GetParent(obj);
-            parent = dObj as T;
-            if (parent != null)
-            {
-                return parent;
-            }
-            else
-            {
-                parent = FindVisualParent<T>(dObj);
-            }
-
-            return parent;
-        }
-
         /// <summary>
         /// 根据行列索引获取单元格
         /// </summary>
@@ -720,9 +673,9 @@ namespace UniformMaterialManagementSystem.ViewModels
             if (row == null) { return; }
 
             // 获取对应行上对应列的单元格
-            DataGridCellsPresenter? cellsPresenter = FindVisualChild<DataGridCellsPresenter>(row);
-            if (cellsPresenter == null) { return; }
-            DataGridCell cell = (DataGridCell)cellsPresenter.ItemContainerGenerator.ContainerFromIndex(columnIndex);
+            DataGridCellsPresenter? cellsPresenter = VisualUtil.FindVisualChild<DataGridCellsPresenter>(row);
+            DataGridCell? cell = cellsPresenter?.ItemContainerGenerator.ContainerFromIndex(columnIndex) as DataGridCell;
+            if (cell == null) { return; }
 
             // 正在编辑的单元格提交编辑
             dataGrid.CommitEdit();

+ 13 - 0
UniformMaterialManagementSystem/Views/DeliveryReceiptControl.xaml.cs

@@ -31,6 +31,8 @@ namespace UniformMaterialManagementSystem.Views
 
             // 绑定 ViewModel
             this.DataContext = App.Current.Services.GetService<DeliveryReceiptViewModel>();
+
+            this.IsVisibleChanged += DeliveryReceiptControl_IsVisibleChanged; ;
         }
 
         public DeliveryReceiptControl(DeliveryPageCategroy categroy)
@@ -50,5 +52,16 @@ namespace UniformMaterialManagementSystem.Views
             // 设置行标题:行索引+1
             e.Row.Header = e.Row.GetIndex() + 1;
         }
+
+        private void DeliveryReceiptControl_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
+        {
+            bool isVisible = (bool)e.NewValue;
+            if (isVisible)
+            {
+                // 每次打开之前重新取数
+                DeliveryReceiptViewModel? vm = this.DataContext as DeliveryReceiptViewModel;
+                vm?.LoadDeliveryData();
+            }
+        }
     }
 }

+ 1 - 2
UniformMaterialManagementSystem/Views/MaterialCompany.xaml.cs

@@ -44,8 +44,7 @@ namespace UniformMaterialManagementSystem.Views
             {
                 // 每次打开页面之前重新取数
                 UsersViewModel? usersViewModel = this.DataContext as UsersViewModel;
-                if (usersViewModel == null) { return; }
-                usersViewModel.LoadData();
+                usersViewModel?.LoadData();
             }
         }
 

+ 1 - 2
UniformMaterialManagementSystem/Views/MaterialControl.xaml.cs

@@ -44,8 +44,7 @@ namespace UniformMaterialManagementSystem.Views
             {
                 // 每次打开页面之前重新取数
                 UsersViewModel? usersViewModel = this.DataContext as UsersViewModel;
-                if (usersViewModel == null) { return; }
-                usersViewModel.LoadData();
+                usersViewModel?.LoadData();
             }
         }
 

+ 3 - 2
UniformMaterialManagementSystem/Views/MaterialReceiptPage.xaml

@@ -258,7 +258,7 @@
                         Tag="{x:Static utils:RegularFontUtil.Clipboard_Checkmark_24}"
                         Template="{StaticResource CustomToolBarButtomTemplate}"
                         Command="{Binding CheckCommand}"
-                        CommandParameter="{Binding ElementName=cbReceivedStatus}" />
+                        CommandParameter="{Binding ElementName=fdgDeliveryDetail}" />
                 <Separator />
                 <Button x:Name="btnExportThree"
                         Content="导出附件3" 
@@ -605,6 +605,7 @@
                             AutoGenerateColumns="False"
                             CanUserAddRows="False"
                             CanUserResizeRows="False"
+                            CanUserReorderColumns="False"
                             SelectionMode="Extended"
                             SelectionUnit="CellOrRowHeader"
                             HeadersVisibility="All"
@@ -616,7 +617,7 @@
                             RowHeaderStyle="{StaticResource CustomRowHeaderStyle}"
                             RowStyle="{StaticResource CustomRowStyle}"
                             CellStyle="{StaticResource CustomCellStyle}"
-                            ItemsSource="{Binding CurrDeliveryReceipt.DeliveryReceiptDetails, Mode=TwoWay}">
+                            ItemsSource="{Binding DeliveryReceiptDetails, Mode=TwoWay}">
 
             <!-- DataGrid 事件 -->
             <b:Interaction.Triggers>

+ 13 - 0
UniformMaterialManagementSystem/Views/MaterialReceiptPage.xaml.cs

@@ -28,6 +28,8 @@ namespace UniformMaterialManagementSystem.Views
 
             // 绑定 ViewModel
             this.DataContext = App.Current.Services.GetService<MaterialReceiptViewModel>();
+
+            this.IsVisibleChanged += MaterialReceiptPage_IsVisibleChanged;
         }
 
         private void DataGrid_LoadingRow(object? sender, DataGridRowEventArgs e)
@@ -35,5 +37,16 @@ namespace UniformMaterialManagementSystem.Views
             // 设置行标题:行索引+1
             e.Row.Header = e.Row.GetIndex() + 1;
         }
+
+        private void MaterialReceiptPage_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
+        {
+            bool isVisible = (bool)e.NewValue;
+            if (isVisible)
+            {
+                // 每次打开之前重新取数
+                MaterialReceiptViewModel? vm = this.DataContext as MaterialReceiptViewModel;
+                vm?.LoadReceiptData();
+            }
+        }
     }
 }

+ 1 - 1
UniformMaterialManagementSystem/Views/SelectApplyContractDialog.xaml.cs

@@ -28,7 +28,7 @@ namespace UniformMaterialManagementSystem.Views
 
             // 绑定 ViewModel 
             this.DataContext = App.Current.Services.GetService<SelectApplyContractDialogViewModel>();
-            
+
             // 注册控件事件
             this.fdgContract.LoadingRow += DataGrid_LoadingRow;
         }

+ 24 - 15
UniformMaterialManagementSystem/Views/UsersControl.xaml.cs

@@ -63,37 +63,44 @@ namespace UniformMaterialManagementSystem.Views
             {
                 // 每次打开之前重新取数
                 UsersViewModel? usersViewModel = this.DataContext as UsersViewModel;
-                if (usersViewModel == null) { return; }
-                usersViewModel.LoadData();
+                usersViewModel?.LoadData();
             }
         }
 
         private void CBSupUnit_SelectionChanged(object sender, SelectionChangedEventArgs e)
         {
             // 需要设置值
-            var comboBox = (ComboBox)sender;
-            SupervisionUnit newSupUnit = (SupervisionUnit)comboBox.SelectedItem;
-            UserModel currUser = (UserModel)dataGridUser.CurrentCell.Item;
-            currUser.SupervisionUnit = newSupUnit;
+            if (sender is not ComboBox comboBox) { return; }
+
+            UserModel? currUser = dataGridUser.CurrentCell.Item as UserModel;
+            if (currUser == null) { return; }
+
+            SupervisionUnit? newSupUnit = comboBox.SelectedItem as SupervisionUnit;
+            if (newSupUnit == null) { return; }
+
             currUser.SupervisionUnitGuid = newSupUnit.Guid;
+            currUser.SupervisionUnit = newSupUnit;
         }
 
         private void CbCompany_SelectionChanged(object sender, SelectionChangedEventArgs e)
         {
             // 需要设置值
-            var comboBox = (ComboBox)sender;
-            Company newCompany = (Company)comboBox.SelectedItem;
+            if (sender is not ComboBox comboBox) { return; }
+
+            UserModel? currUser = dataGridUser.CurrentCell.Item as UserModel;
+            if (currUser == null) { return; }
+
+            Company? newCompany = comboBox.SelectedItem as Company;
+            if (newCompany == null) { return; }
 
-            var currCell = this.dataGridUser.CurrentCell;
-            UserModel currUser = (UserModel)currCell.Item;
             currUser.CompanyGuid = newCompany.Guid;
             currUser.Company = newCompany;
         }
 
         private void CBMultiRole_Loaded(object sender, RoutedEventArgs e)
         {
-            var cbMultiRole = sender as ComboBox;
-            if (cbMultiRole == null) { return; }
+            if (sender is not ComboBox cbMultiRole) { return; }
+
             UserModel? currUser = dataGridUser.CurrentCell.Item as UserModel;
             if (currUser == null || currUser.Roles == null || currUser.Roles.Count == 0) { return; }
             // 设置当前行角色列的显示文本
@@ -103,10 +110,12 @@ namespace UniformMaterialManagementSystem.Views
         private void RoleCheckBox_Loaded(object sender, RoutedEventArgs e)
         {
             // DropDownOpened 事件结束之后执行
-            var checkBox = sender as CheckBox;
-            if (checkBox == null) { return; }
+            if (sender is not CheckBox checkBox) { return; }
+
             string? tag = checkBox.Tag.ToString(); // TextBlock
-            UserModel? currItem = (UserModel)dataGridUser.CurrentItem;
+            UserModel? currItem = dataGridUser.CurrentItem as UserModel;
+            if (currItem == null) { return; }
+
             string[] selectedRoleDescs = currItem.Roles.Select(x => x.Description).ToArray();
             if (selectedRoleDescs.Contains(tag))
             {