Procházet zdrojové kódy

feat: 材料企业取消必录;材料发货实现部分功能

JaneDoe před 3 měsíci
rodič
revize
5433235711

+ 4 - 0
UniformMaterialManagementSystem/App.xaml.cs

@@ -61,6 +61,8 @@ namespace UniformMaterialManagementSystem
             services.AddKeyedTransient<UserControl, DeliveryReceiptControl>("DeliveryReceiptControl");
             services.AddKeyedTransient<UserControl, InspectionReportPage>("InspectionReportPage");
             services.AddKeyedTransient<UserControl, SampleRegistrationPage>("SampleRegistrationPage");
+services.AddKeyedTransient<UserControl, SelectApplyContractDialog>("SelectApplyContractDialog");
+services.AddKeyedTransient<UserControl, InspectionOrganizationPage>("InspectionOrganizationPage");
             services.AddKeyedTransient<UserControl, InspectionOrganizationPage>("InspectionOrganizationPage");
             services.AddKeyedTransient<UserControl, FactoryLicensePage>("FactoryLicensePage");
 
@@ -85,6 +87,8 @@ namespace UniformMaterialManagementSystem
             services.AddTransient<SampleRegistrationPageViewModel>();
             services.AddTransient<InspectionOrganizationPageViewModel>();
             services.AddTransient<FactoryLicensePageViewModel>();
+services.AddTransient<SelectApplyContractDialogViewModel>();
+services.AddTransient<InspectionOrganizationPageViewModel>();
 
             // Service 注册
             services.AddScoped(typeof(IDataBaseService<>), typeof(DataBaseService<>));

+ 19 - 6
UniformMaterialManagementSystem/Entities/DeliveryReceipt.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -13,19 +14,25 @@ namespace UniformMaterialManagementSystem.Entities
         [Key]
         public Guid Guid { get; set; } = Guid.NewGuid();
 
-        public Guid ApplyGuid { get; set; } = default!;
+        public string ApplyNo { get; set; } = string.Empty;
 
         public Guid ContractGuid { get; set; } = default!;
 
-        public string ProductCompanyName { get; set; } = default!;
+        public string PurchaseCompanyName { get; set; } = default!;
 
         public string ContractNo { get; set; } = string.Empty;
 
-        public decimal ContractQty { get; set; } = 0;
+        public double ContractQty { get; set; } = 0!;
+
+        [NotMapped]
+        public double InspQty { get; set; }
+
+        [NotMapped]
+        public double RemainQty { get; set; }
 
         public DateTime ContractSigningDate { get; set; } = default!;
 
-        public string MaterialName { get; set; } = null!;
+        public string ProductName { get; set; } = null!;
 
         public string CompanyName { get; set; } = null!;
 
@@ -36,7 +43,7 @@ namespace UniformMaterialManagementSystem.Entities
         // todo default! 0! 0 三种情况分别表示什么意思?在这用什么比较合适
         public int ShippedPackets { get; set; } = 0!;
 
-        public decimal ShippedQty { get; set; } = decimal.Zero;
+        public double ShippedQty { get; set; } = 0!;
 
         public DateTime ShippedDate { get; set; } = DateTime.Now.Date;
 
@@ -46,7 +53,7 @@ namespace UniformMaterialManagementSystem.Entities
 
         public int? ReceivedPackets { get; set; }
 
-        public decimal? ReceivedQty { get; set; }
+        public double? ReceivedQty { get; set; }
 
         public DateTime? ReceivedDate { get; set; }
 
@@ -58,6 +65,12 @@ namespace UniformMaterialManagementSystem.Entities
 
         public byte[]? Licence { get; set; } = null!;
 
+        [NotMapped]
+        public bool IsNewRow { get; set; } = true;
+
+        [NotMapped]
+        public bool IsReported { get; set; } = false;
+
         // 发货、接收和使用使用同一张表,明细也用同一张表,三个模块分别控制不同的必录项
         public ObservableCollection<DeliveryReceiptDetail> DeliveryReceiptDetails { get; set; } = [];
 

+ 7 - 3
UniformMaterialManagementSystem/Entities/DeliveryReceiptDetail.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.ComponentModel.DataAnnotations;
+using System.ComponentModel.DataAnnotations.Schema;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
@@ -16,12 +17,15 @@ namespace UniformMaterialManagementSystem.Entities
 
         public string PacketNo { get; set; } = null!;
         
-        public decimal ShippedQuantity { get; set; } = decimal.Zero;
+        public double ShippedQuantity { get; set; } = 0!;
 
-        public decimal? ReceiveQuantity { get; set; }
+        public double? ReceiveQuantity { get; set; }
 
-        public decimal? UseQuantity { get; set; }
+        public double? UseQuantity { get; set; }
 
         public string? UseStatus { get; set; }
+
+        [NotMapped]
+        public bool IsNewRow { get; set; } = true;
     }
 }

+ 1135 - 0
UniformMaterialManagementSystem/Migrations/20240802063820_modify_DeliveryReceipt.Designer.cs

@@ -0,0 +1,1135 @@
+// <auto-generated />
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using UniformMaterialManagementSystem.Data;
+
+#nullable disable
+
+namespace UniformMaterialManagementSystem.Migrations
+{
+    [DbContext(typeof(SqliteContext))]
+    [Migration("20240802063820_modify_DeliveryReceipt")]
+    partial class modify_DeliveryReceipt
+    {
+        /// <inheritdoc />
+        protected override void BuildTargetModel(ModelBuilder modelBuilder)
+        {
+#pragma warning disable 612, 618
+            modelBuilder.HasAnnotation("ProductVersion", "7.0.20");
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.Company", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Address")
+                        .HasMaxLength(100)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Category")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Code")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("IsEnabled")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Manager")
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Note")
+                        .HasMaxLength(100)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ShortName")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("SystemCode")
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.ToTable("Companies");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.Contract", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<byte[]>("Attachment")
+                        .HasColumnType("BLOB");
+
+                    b.Property<Guid>("CompanyGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ContractNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("EditDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("EditUser")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("ExportStatus")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid>("PurchaseCompanyGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("SigningDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Telephone")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("CompanyGuid");
+
+                    b.HasIndex("PurchaseCompanyGuid");
+
+                    b.ToTable("Contracts");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.ContractDetail", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("ContractGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<double>("ContractQty")
+                        .HasColumnType("REAL");
+
+                    b.Property<DateTime>("DeliveryTime")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("InspectStatus")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<double>("InspectedQty")
+                        .HasColumnType("REAL");
+
+                    b.Property<Guid>("MaterialGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<double>("ShippedQuantity")
+                        .HasColumnType("REAL");
+
+                    b.Property<double>("UnitPrice")
+                        .HasColumnType("REAL");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("ContractGuid");
+
+                    b.HasIndex("MaterialGuid");
+
+                    b.ToTable("ContractDetails");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.DeliveryReceipt", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ApplyNo")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("BatchNo")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("CompanyName")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("ContractGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ContractNo")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.Property<decimal>("ContractQty")
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("ContractSigningDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<byte[]>("Licence")
+                        .HasColumnType("BLOB");
+
+                    b.Property<string>("ProductName")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("PurchaseCompanyName")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ReceivedCompanyName")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime?>("ReceivedDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ReceivedMan")
+                        .HasColumnType("TEXT");
+
+                    b.Property<int?>("ReceivedPackets")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<decimal?>("ReceivedQty")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ReceivedStatus")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ReceivedTel")
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("ShippedDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ShippedMan")
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("ShippedPackets")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<decimal>("ShippedQty")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ShippedTel")
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.ToTable("DeliveryReceipts");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.DeliveryReceiptDetail", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("DeliveryReceiptGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("PacketNo")
+                        .IsRequired()
+                        .HasColumnType("TEXT");
+
+                    b.Property<decimal?>("ReceiveQuantity")
+                        .HasColumnType("TEXT");
+
+                    b.Property<decimal>("ShippedQuantity")
+                        .HasColumnType("TEXT");
+
+                    b.Property<decimal?>("UseQuantity")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("UseStatus")
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("DeliveryReceiptGuid");
+
+                    b.ToTable("DeliveryReceiptDetails");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectApply", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ApplyDescription")
+                        .HasMaxLength(1000)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ApplyNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime?>("ApplyReceiveDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ApplyUser")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("BatchNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Company")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("CompanyGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("EndProductDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("InspCategory")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("InspDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<double>("InspQuantity")
+                        .HasColumnType("REAL");
+
+                    b.Property<byte[]>("InspReport")
+                        .HasColumnType("BLOB");
+
+                    b.Property<string>("InspReportNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("LicenseStatus")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid>("MaterialGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ProductName")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("ReportStatus")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("SampleStatus")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<DateTime>("StartProductDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("Year")
+                        .HasColumnType("INTEGER");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("CompanyGuid");
+
+                    b.HasIndex("MaterialGuid");
+
+                    b.ToTable("InspectApplies");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectApplyContractDetail", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("ApplyGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ApplyNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("ContractDetailGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ContractNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<double>("InspectQty")
+                        .HasColumnType("REAL");
+
+                    b.Property<string>("PurchaseCompany")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("PurchaseCompanyShortName")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("SigningDate")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<double>("UnInspectQty")
+                        .HasColumnType("REAL");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("ApplyGuid");
+
+                    b.ToTable("InspectApplyContractDetails");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectApplyDetail", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("ApplyGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ApplyNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Note")
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("PacketNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<double>("Quantity")
+                        .HasColumnType("REAL");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("ApplyGuid");
+
+                    b.ToTable("InspectApplyDetails");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectionOrganization", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Address")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Contacts")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("IsEnabled")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("OrderNo")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Telephone")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.ToTable("InspectionOrganizations");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectionReport", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Conclusion")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ConclusionDesc")
+                        .IsRequired()
+                        .HasMaxLength(1000)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Department")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("EditUser")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("InspectApplyGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("IsSample")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("ReportBasis")
+                        .IsRequired()
+                        .HasMaxLength(200)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ReportDesc")
+                        .IsRequired()
+                        .HasMaxLength(1000)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ReportNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("ReportTime")
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("InspectApplyGuid");
+
+                    b.ToTable("InspectionReports");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectionReportDetail", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("InspectionReportGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Inspector")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("JobCategory")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("SupervisionUnit")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("InspectionReportGuid");
+
+                    b.ToTable("InspectionReportDetails");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.Material", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("CategoryCode")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("IsEnabled")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("MeasureUnit")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("NormName")
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Note")
+                        .HasMaxLength(100)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Specification")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("VarietyCode")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.ToTable("Materials");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.MenuItem", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Icon")
+                        .HasMaxLength(100)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("IsEnabled")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<bool>("IsVisible")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("OrderIndex")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<Guid?>("ParentGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Path")
+                        .HasMaxLength(100)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("ParentGuid");
+
+                    b.ToTable("MenuItems");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.Role", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Description")
+                        .IsRequired()
+                        .HasMaxLength(100)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("IsEnabled")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.ToTable("Roles");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.RoleMenuItem", b =>
+                {
+                    b.Property<Guid>("MenuItemGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("RoleGuid")
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("MenuItemGuid", "RoleGuid");
+
+                    b.HasIndex("RoleGuid");
+
+                    b.ToTable("RoleMenuItems");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.SampleRegistration", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("BatchNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Department")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("EditTime")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("EditUser")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("InspectApplyGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("InspectionOrganization")
+                        .IsRequired()
+                        .HasMaxLength(500)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("PacketNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("ProductDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ProductUsers")
+                        .HasMaxLength(100)
+                        .HasColumnType("TEXT");
+
+                    b.Property<double>("Quantity")
+                        .HasColumnType("REAL");
+
+                    b.Property<string>("SampleNo")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("SingleIndexItem")
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Telephone")
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("TestingItem")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("InspectApplyGuid");
+
+                    b.ToTable("SampleRegistrations");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.SerialNumber", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("CompanyCode")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("CompanyGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("InspCategory")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("MaxNumber")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("SupervisionUnitGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<int>("Year")
+                        .HasColumnType("INTEGER");
+
+                    b.HasKey("Guid");
+
+                    b.ToTable("SerialNumbers");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.SupervisionUnit", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("IsEnabled")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("Name")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Note")
+                        .HasMaxLength(100)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("ShortName")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.ToTable("SupervisionUnits");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.User", b =>
+                {
+                    b.Property<Guid>("Guid")
+                        .ValueGeneratedOnAdd()
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid?>("CompanyGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("CompanyName")
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("IdNumber")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<bool>("IsEnabled")
+                        .HasColumnType("INTEGER");
+
+                    b.Property<string>("LoginName")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Password")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<DateTime>("RegisterDate")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("Salt")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("SupervisionUnitGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<string>("UserName")
+                        .IsRequired()
+                        .HasMaxLength(50)
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("Guid");
+
+                    b.HasIndex("CompanyGuid");
+
+                    b.HasIndex("SupervisionUnitGuid");
+
+                    b.ToTable("Users");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.UserRole", b =>
+                {
+                    b.Property<Guid>("RoleGuid")
+                        .HasColumnType("TEXT");
+
+                    b.Property<Guid>("UserGuid")
+                        .HasColumnType("TEXT");
+
+                    b.HasKey("RoleGuid", "UserGuid");
+
+                    b.HasIndex("UserGuid");
+
+                    b.ToTable("UserRoles");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.Contract", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.Company", "Company")
+                        .WithMany()
+                        .HasForeignKey("CompanyGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("UniformMaterialManagementSystem.Entities.Company", "PurchaseCompany")
+                        .WithMany()
+                        .HasForeignKey("PurchaseCompanyGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Company");
+
+                    b.Navigation("PurchaseCompany");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.ContractDetail", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.Contract", "Contract")
+                        .WithMany("ContractDetails")
+                        .HasForeignKey("ContractGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("UniformMaterialManagementSystem.Entities.Material", "Material")
+                        .WithMany()
+                        .HasForeignKey("MaterialGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Contract");
+
+                    b.Navigation("Material");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.DeliveryReceiptDetail", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.DeliveryReceipt", null)
+                        .WithMany("DeliveryReceiptDetails")
+                        .HasForeignKey("DeliveryReceiptGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectApply", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.Company", "MaterialCompany")
+                        .WithMany()
+                        .HasForeignKey("CompanyGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("UniformMaterialManagementSystem.Entities.Material", "Material")
+                        .WithMany()
+                        .HasForeignKey("MaterialGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Material");
+
+                    b.Navigation("MaterialCompany");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectApplyContractDetail", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.InspectApply", "InspectApply")
+                        .WithMany("InspectApplyContractDetails")
+                        .HasForeignKey("ApplyGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("InspectApply");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectApplyDetail", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.InspectApply", "InspectApply")
+                        .WithMany("InspectApplyDetails")
+                        .HasForeignKey("ApplyGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("InspectApply");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectionReport", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.InspectApply", "InspectApply")
+                        .WithMany()
+                        .HasForeignKey("InspectApplyGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("InspectApply");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectionReportDetail", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.InspectionReport", "InspectionReport")
+                        .WithMany("InspectionReportDetails")
+                        .HasForeignKey("InspectionReportGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("InspectionReport");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.MenuItem", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.MenuItem", "Parent")
+                        .WithMany("Children")
+                        .HasForeignKey("ParentGuid")
+                        .OnDelete(DeleteBehavior.Restrict);
+
+                    b.Navigation("Parent");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.RoleMenuItem", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.MenuItem", "MenuItem")
+                        .WithMany("MenuRoles")
+                        .HasForeignKey("MenuItemGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("UniformMaterialManagementSystem.Entities.Role", "Role")
+                        .WithMany("RoleMenus")
+                        .HasForeignKey("RoleGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("MenuItem");
+
+                    b.Navigation("Role");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.SampleRegistration", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.InspectApply", "InspectApply")
+                        .WithMany()
+                        .HasForeignKey("InspectApplyGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("InspectApply");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.User", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.Company", "Company")
+                        .WithMany()
+                        .HasForeignKey("CompanyGuid");
+
+                    b.HasOne("UniformMaterialManagementSystem.Entities.SupervisionUnit", "SupervisionUnit")
+                        .WithMany()
+                        .HasForeignKey("SupervisionUnitGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Company");
+
+                    b.Navigation("SupervisionUnit");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.UserRole", b =>
+                {
+                    b.HasOne("UniformMaterialManagementSystem.Entities.Role", "Role")
+                        .WithMany("UserRoles")
+                        .HasForeignKey("RoleGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.HasOne("UniformMaterialManagementSystem.Entities.User", "User")
+                        .WithMany("UserRoles")
+                        .HasForeignKey("UserGuid")
+                        .OnDelete(DeleteBehavior.Cascade)
+                        .IsRequired();
+
+                    b.Navigation("Role");
+
+                    b.Navigation("User");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.Contract", b =>
+                {
+                    b.Navigation("ContractDetails");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.DeliveryReceipt", b =>
+                {
+                    b.Navigation("DeliveryReceiptDetails");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectApply", b =>
+                {
+                    b.Navigation("InspectApplyContractDetails");
+
+                    b.Navigation("InspectApplyDetails");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.InspectionReport", b =>
+                {
+                    b.Navigation("InspectionReportDetails");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.MenuItem", b =>
+                {
+                    b.Navigation("Children");
+
+                    b.Navigation("MenuRoles");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.Role", b =>
+                {
+                    b.Navigation("RoleMenus");
+
+                    b.Navigation("UserRoles");
+                });
+
+            modelBuilder.Entity("UniformMaterialManagementSystem.Entities.User", b =>
+                {
+                    b.Navigation("UserRoles");
+                });
+#pragma warning restore 612, 618
+        }
+    }
+}

+ 48 - 0
UniformMaterialManagementSystem/Migrations/20240802063820_modify_DeliveryReceipt.cs

@@ -0,0 +1,48 @@
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace UniformMaterialManagementSystem.Migrations
+{
+    /// <inheritdoc />
+    public partial class modify_DeliveryReceipt : Migration
+    {
+        /// <inheritdoc />
+        protected override void Up(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.RenameColumn(
+                name: "ProductCompanyName",
+                table: "DeliveryReceipts",
+                newName: "PurchaseCompanyName");
+
+            migrationBuilder.RenameColumn(
+                name: "MaterialName",
+                table: "DeliveryReceipts",
+                newName: "ProductName");
+
+            migrationBuilder.RenameColumn(
+                name: "ApplyGuid",
+                table: "DeliveryReceipts",
+                newName: "ApplyNo");
+        }
+
+        /// <inheritdoc />
+        protected override void Down(MigrationBuilder migrationBuilder)
+        {
+            migrationBuilder.RenameColumn(
+                name: "PurchaseCompanyName",
+                table: "DeliveryReceipts",
+                newName: "ProductCompanyName");
+
+            migrationBuilder.RenameColumn(
+                name: "ProductName",
+                table: "DeliveryReceipts",
+                newName: "MaterialName");
+
+            migrationBuilder.RenameColumn(
+                name: "ApplyNo",
+                table: "DeliveryReceipts",
+                newName: "ApplyGuid");
+        }
+    }
+}

+ 4 - 3
UniformMaterialManagementSystem/Migrations/SqliteContextModelSnapshot.cs

@@ -162,7 +162,8 @@ namespace UniformMaterialManagementSystem.Migrations
                         .ValueGeneratedOnAdd()
                         .HasColumnType("TEXT");
 
-                    b.Property<Guid>("ApplyGuid")
+                    b.Property<string>("ApplyNo")
+                        .IsRequired()
                         .HasColumnType("TEXT");
 
                     b.Property<string>("BatchNo")
@@ -189,11 +190,11 @@ namespace UniformMaterialManagementSystem.Migrations
                     b.Property<byte[]>("Licence")
                         .HasColumnType("BLOB");
 
-                    b.Property<string>("MaterialName")
+                    b.Property<string>("ProductName")
                         .IsRequired()
                         .HasColumnType("TEXT");
 
-                    b.Property<string>("ProductCompanyName")
+                    b.Property<string>("PurchaseCompanyName")
                         .IsRequired()
                         .HasColumnType("TEXT");
 

+ 632 - 67
UniformMaterialManagementSystem/ViewModels/DeliveryReceiptViewModel.cs

@@ -2,33 +2,63 @@
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.ComponentModel;
+using System.Data;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
 using System.Windows;
 using System.Windows.Controls;
+using System.Windows.Media.Media3D;
 using CommunityToolkit.Mvvm.ComponentModel;
 using CommunityToolkit.Mvvm.Input;
+using DocumentFormat.OpenXml.Spreadsheet;
 using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Win32;
+using UniformMaterialManagementSystem.Custom;
 using UniformMaterialManagementSystem.Entities;
 using UniformMaterialManagementSystem.Services;
+using UniformMaterialManagementSystem.Utils;
+using UniformMaterialManagementSystem.Views;
 
 namespace UniformMaterialManagementSystem.ViewModels
 {
     public partial class DeliveryReceiptViewModel : ObservableObject
     {
-        [ObservableProperty]
-        private ObservableCollection<InspectApply> _inspectApplies = new ObservableCollection<InspectApply>();
+        #region 数据绑定字段
 
         [ObservableProperty]
-        private ObservableCollection<InspectApplyContractDetail> _inspectApplyContracts = new ObservableCollection<InspectApplyContractDetail>();
+        private List<string> _inspectApplyNos = new List<string>();
+
+        //[ObservableProperty]
+        //private List<string> _companyNames = new List<string>();
+
+        //[ObservableProperty]
+        //private List<string> _productNames = new List<string>();
+
+        //[ObservableProperty]
+        //private ObservableCollection<InspectApplyContractDetail> _inspectApplyContracts = new ObservableCollection<InspectApplyContractDetail>();
 
         [ObservableProperty]
         private ObservableCollection<DeliveryReceipt> _deliveryReceipts = new ObservableCollection<DeliveryReceipt>();
 
         [ObservableProperty]
-        private DeliveryReceipt _currDeliveryReceipt = new DeliveryReceipt();
+        private ObservableCollection<DeliveryReceiptDetail> _deliveryDetails = new ObservableCollection<DeliveryReceiptDetail>();
+
+        [ObservableProperty]
+        private DeliveryReceipt? _currDeliveryReceipt = null;
+
+        //[ObservableProperty]
+        //private double _inspQty = 0;
+
+        //[ObservableProperty]
+        //private double _remainQty = 0;
+
+        #endregion
+
+        private InspectApply _selectedApply = null!;
+
+        private InspectApplyContractDetail _selectedContractDetail = null!;
 
         private IDataBaseService<DeliveryReceipt> _service;
 
@@ -41,99 +71,634 @@ namespace UniformMaterialManagementSystem.ViewModels
             LoadData();
         }
 
-        [RelayCommand]
-        public void ApplySelectionChanged(DataGrid dataGridApply)
-        {
-            // 获取当前行的合同信息
-            InspectApply? currApply = dataGridApply.SelectedItem as InspectApply;
-            if (currApply == null) { return; }
-
-            InspectApplyContracts.Clear();
-            // !不能直接赋值,否则清空后会把 currApply 的明细也清空
-            //InspectApplyContracts = currApply.InspectApplyContractDetails; 
-            InspectApplyContracts = new ObservableCollection<InspectApplyContractDetail>(currApply.InspectApplyContractDetails);
-        }
+        //[RelayCommand]
+        //public void Query(AutoCompleteBox autoListBoxNo)
+        //{
+        //    //object[]? values = parameters as object[];
+        //    //if (values == null || values.Length != 2) { return; }
+        //    //AutoCompleteBox? autoListBoxNo = values[0] as AutoCompleteBox;
+        //    //AutoCompleteBox? autoListBoxMate = values[1] as AutoCompleteBox;
+        //    //if (autoListBoxNo == null || autoListBoxMate == null) { return; }
 
-        [RelayCommand]
-        public void ContractSelectionChanged(DataGrid dataGridContract)
+        //    //// 根据 申请编号 和 材料名称 查询数据:为空获取全部数据
+        //    //string? applyNo = autoListBoxNo.Text;
+        //    //string? mateName = autoListBoxMate.Text;
+        //    //string predicate = string.Empty;
+        //    //if (!string.IsNullOrEmpty(applyNo))
+        //    //{
+        //    //    predicate = $"x => x.ApplyNo.Equals({applyNo})";
+        //    //}
+        //    //if(!string.IsNullOrEmpty(mateName))
+        //    //{
+        //    //    predicate = predicate.Length == 0 ? $"x => x.ApplyNo.Equals({applyNo})" : predicate + "and x => x.ApplyNo.Equals({applyNo})";
+        //    //}
+
+        //    // 根据选择的 检验申请编号 获取合同数据
+        //    if (autoListBoxNo == null) { return; }
+        //    //string? applyNo = autoListBoxNo.Text; // Text 属性更新不及时,SelectedItem更新及时
+        //    string? applyNo = autoListBoxNo.SelectedItem == null ? string.Empty : autoListBoxNo.SelectedItem.ToString();
+        //    IQueryable<InspectApplyContractDetail>? contracts = null;
+        //    if (!string.IsNullOrEmpty(applyNo))
+        //    {
+        //        // todo 会不会出现当前合同对应多个检验申请
+        //        contracts = App.Current.Services.GetService<IDataBaseService<InspectApplyContractDetail>>()?.Query(x => x.ApplyNo.Equals(applyNo)).OrderBy(x => x.ContractNo);
+        //    }
+        //    else
+        //    {
+        //        // 没有设置值则获取全部合同明细
+        //        contracts = App.Current.Services.GetService<IDataBaseService<InspectApplyContractDetail>>()?.Query().OrderBy(x => x.ContractNo);
+        //    }
+
+        //    if (contracts == null || !contracts.Any())
+        //    {
+        //        MessageBox.Show("获取合同数据失败!");
+        //        return;
+        //    }
+        //    InspectApplyContracts = new ObservableCollection<InspectApplyContractDetail>(contracts);
+        //}
+
+        // CallMethod 可以传递事件参数,但没办法屏蔽事件
+        public void DeliverySelectionChanged(object sender, SelectionChangedEventArgs args)
         {
-            // 获取合同明细下的发货单信息
-            var currContract = dataGridContract.SelectedItem as InspectApplyContractDetail;
-            if (currContract == null) { return; }
-
-            //var currApply = currContract.InspectApply;
-            DeliveryReceipts.Clear();
-            var receipts = _service.Query(x => x.ApplyGuid.Equals(currContract.ApplyGuid) && x.ContractGuid.Equals(currContract.Guid));
-            DeliveryReceipts = new ObservableCollection<DeliveryReceipt>(receipts);
+            DataGrid? fdgDelivery = sender as DataGrid;
+            if (fdgDelivery == null) { return; }
+
+
+            // 离开行之前,判断当前行是否未保存
+            if (args.RemovedItems.Count > 0)
+            {
+                DeliveryReceipt? leavingDelivery = args.RemovedItems[0] as DeliveryReceipt;
+                if (leavingDelivery == null) { return; }
+                bool isChanged = IsChanged(leavingDelivery);
+                if (isChanged)
+                {
+                    // 询问是否切换
+                    MessageBoxResult res = MessageBox.Show("当前行已修改,切换行会丢失已修改的数据,是否确认切换行?", "询问", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
+                    // 不切换:需要重新赋值
+                    if (res != MessageBoxResult.Yes)
+                    {
+                        // 取消切换行:当前行不变
+                        fdgDelivery.SelectionChanged -= DeliverySelectionChanged;
+
+                        fdgDelivery.SelectedItem = leavingDelivery; // 值已经更新为切换后的行,所以需要重新赋值为旧行
+                        // 直接赋值会循环调用本事件,CurrDeliveryReceipt 双向绑定也会循环调用
+                        CurrDeliveryReceipt = leavingDelivery; // 值仍为旧行? CurrDeliveryReceipt 双向绑定?
+
+                        fdgDelivery.SelectionChanged += DeliverySelectionChanged;
+
+                        DeliveryDetails = leavingDelivery.DeliveryReceiptDetails; // 值仍为旧行明细
+                        return;
+                    }
+                    // 切换:继续执行,到 展示新行数据的部分
+                }
+            }
+
+            // 进入新的行时,界面展示新行数据
+            if (args.AddedItems.Count > 0)
+            {
+                DeliveryReceipt? newDelivery = args.AddedItems[0] as DeliveryReceipt;
+                if (newDelivery == null) { return; }
+                //fdgDelivery.SelectedItem = newDelivery; // 二者相等? 已经是 SelectionChanged,所以相等?
+                //CurrDeliveryReceipt = newDelivery; // 相等?
+                //DeliveryDetails = newDelivery.DeliveryReceiptDetails; // 相等?
+            }
         }
 
         [RelayCommand]
-        public void AddDelivery(object parameters)
+        public void AddDelivery()
         {
-            object[]? values = parameters as object[];
-            if (values == null || values.Length != 2) { return; }
-            DataGrid? dataGridContract = values[0] as DataGrid;
-            DataGrid? dataGridDelivery = values[1] as DataGrid;
-            if (dataGridContract == null || dataGridDelivery == null) { return; }
+            // todo 一行一保存:行切换时检查是否新增行未保存/保存行已修改
 
-            InspectApplyContractDetail? currContract = dataGridContract.SelectedItem as InspectApplyContractDetail;
-            InspectApply? currApply = currContract?.InspectApply as InspectApply;
-            if (currApply == null || currContract == null)
-            {
-                MessageBox.Show("请先选择一条检验申请,以及一条合同明细!");
-                return;
-            }
+            // 弹出窗口:选择检验申请和合同明细
+            var window = new Window();
+            SelectApplyContractDialog dialog = new SelectApplyContractDialog();
+            window.Content = dialog;
+            window.ShowDialog();
 
-            DeliveryReceipts.Add(CurrDeliveryReceipt);
+            // 若没有选择(取消 or 直接关闭对话框),则直接返回
+            SelectApplyContractDialogViewModel? dialogVM = dialog.DataContext as SelectApplyContractDialogViewModel;
+            if (dialogVM?.SelectedApply == null || dialogVM.SelectedContract == null) { return; }
+            _selectedApply = dialogVM.SelectedApply;
+            _selectedContractDetail = dialogVM.SelectedContract;
+
+            // 获取当前合同信息的已发运数量
+            string applyNo = _selectedApply.ApplyNo;
+            string contractNo = _selectedContractDetail.ContractNo;
+            double hasShippedQty = (double)DeliveryReceipts.Where(x => x.ApplyNo.Equals(applyNo) && x.ContractNo.Equals(contractNo)).Select(x => x.ShippedQty).Sum();
 
+            // 新增发货单
+            CurrDeliveryReceipt = new DeliveryReceipt();
+            DeliveryReceipts.Add(CurrDeliveryReceipt);
+            // 检验申请编号
+            CurrDeliveryReceipt.ApplyNo = _selectedApply.ApplyNo;
             // 产品名称
-            CurrDeliveryReceipt.MaterialName = currApply.ProductName;
+            CurrDeliveryReceipt.ProductName = _selectedApply.ProductName;
+            // todo 只能选择当前生产企业的合同?
             // 生产企业
-            CurrDeliveryReceipt.CompanyName = "生产企业11";
-            // 采购机构
-            CurrDeliveryReceipt.ProductCompanyName = "采购机构11";
+            CurrDeliveryReceipt.CompanyName = _selectedApply.Company;
             // 合同编号
-            CurrDeliveryReceipt.ContractNo = currContract.ContractNo;
+            CurrDeliveryReceipt.ContractNo = contractNo;
+            // 采购机构
+            CurrDeliveryReceipt.PurchaseCompanyName = _selectedContractDetail.PurchaseCompany;
             // 合同数量
-            CurrDeliveryReceipt.ContractQty = (decimal)currContract.InspectQty;
+            CurrDeliveryReceipt.ContractQty = _selectedContractDetail.InspectQty;
             // 合同时间
-            CurrDeliveryReceipt.ContractSigningDate = Convert.ToDateTime(currContract.SigningDate);
-            // 接收单位
-            CurrDeliveryReceipt.ReceivedCompanyName = "接收单位";
+            CurrDeliveryReceipt.ContractSigningDate = Convert.ToDateTime(_selectedContractDetail.SigningDate);
+            // 检验合格数量:检验申请主表-报检数量
+            CurrDeliveryReceipt.InspQty = _selectedApply.InspQuantity;
+            // 可发运数量:检验申请主表-报检数量 - 已发运数量
+            CurrDeliveryReceipt.RemainQty = CurrDeliveryReceipt.InspQty - hasShippedQty;
+            // 接收单位:等于合同-采购机构
+            CurrDeliveryReceipt.ReceivedCompanyName = _selectedContractDetail.PurchaseCompany;
             // 批次号
-            // todo  批次号放表头
-            CurrDeliveryReceipt.BatchNo = "todo";
-            // 发运包数
-            CurrDeliveryReceipt.ShippedPackets = 500;
-            // 发运数量
-            CurrDeliveryReceipt.ShippedQty = 100;
-            // 发运时间
+            CurrDeliveryReceipt.BatchNo = _selectedApply.BatchNo;
+            // 发运时间:默认当前日期
             CurrDeliveryReceipt.ShippedDate = DateTime.Now.Date;
-            // 发运承办人
+            // 发运承办人:默认当前用户的姓名
             CurrDeliveryReceipt.ShippedMan = App.CurrentUser?.UserName;
-            // 联系电话
-            CurrDeliveryReceipt.ShippedTel = "Tel";
+            // 发运包数:自动汇总子表
+            // 发运数量:自动汇总子表
+            // 联系电话:手动输入
 
             OnPropertyChanged(CommunityToolkit.Mvvm.ComponentModel.__Internals.__KnownINotifyPropertyChangedArgs.CurrDeliveryReceipt);
         }
 
-        private void LoadData()
+        [RelayCommand]
+        public void DeleteDelivery(DataGrid fdgDelivery)
         {
+            var selected = fdgDelivery.SelectedItem;
+            var curr = CurrDeliveryReceipt;
+            var details = DeliveryDetails;
+
+            // 没有选中行直接删除
             if (CurrDeliveryReceipt == null)
             {
-                CurrDeliveryReceipt = new DeliveryReceipt();
+                MessageBox.Show("请选择一条发货单!");
+                return;
+            }
+
+            // 已导出数据包不允许删除(不允许编辑)
+            if (CurrDeliveryReceipt.IsReported)
+            {
+                MessageBox.Show("当前发货单已导出数据包,不允许删除!");
+                return;
+            }
+
+            // 新增行直接删除
+            if (CurrDeliveryReceipt.IsNewRow)
+            {
+                DeliveryReceipts.Remove(CurrDeliveryReceipt);
+                CurrDeliveryReceipt = null;
+                return;
+            }
+
+            // 判断已保存行是否已修改
+            bool isChanged = IsChanged(CurrDeliveryReceipt);
+            if (isChanged)
+            {
+                MessageBoxResult res = MessageBox.Show("当前发货单已修改,是否确定删除?", "询问", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
+                if (res != MessageBoxResult.Yes)
+                {
+                    return;
+                }
+
+                // 界面删除
+                DeliveryReceipts.Remove(CurrDeliveryReceipt);
+
+                // 保存到数据库
+                _service.Delete(CurrDeliveryReceipt);
+                bool? isSuccess = _service.SaveChanges();
+                if ((bool)isSuccess)
+                {
+                    MessageBox.Show("删除成功");
+                }
+                else
+                {
+                    MessageBox.Show("删除失败!");
+                }
+            }
+        }
+
+        [RelayCommand]
+        public void Save()
+        {
+            // 当前行未导出数据包才允许保存
+            if (CurrDeliveryReceipt == null) { return; }
+            if (CurrDeliveryReceipt.IsReported) { return; }
+
+            // todo 清空日期选择框,但 发运日期 并不会清空
+            // 检查发运时间是否已录入
+            if (CurrDeliveryReceipt.ShippedDate == null)
+            {
+                MessageBox.Show("发运时间为空,不允许保存!");
+                return;
+            }
+
+            // 删除子表空行
+            DeleteEmptyRows();
+
+            // todo 子表新增行和删除行和修改列时实时计算
+            // 计算发运包数和发运数量
+            int packNum = CurrDeliveryReceipt.DeliveryReceiptDetails.Count;
+            CurrDeliveryReceipt.ShippedPackets = packNum;
+            double shippedQty = CurrDeliveryReceipt.DeliveryReceiptDetails.Select(x => x.ShippedQuantity).Sum();
+            CurrDeliveryReceipt.ShippedQty = shippedQty;
+
+            // 检查发运数量不能超出可发运数量
+            if (CurrDeliveryReceipt.ShippedQty >= CurrDeliveryReceipt.RemainQty)
+            {
+                MessageBox.Show("发货数量超出可发运数量,请检查数据后重新保存!");
+                return;
+            }
+
+            // 检查子表的包号不允许重复
+            // 保存时检查材料名称是否存在重复项
+            var repeatPacketNos = CurrDeliveryReceipt.DeliveryReceiptDetails.GroupBy(x => x.PacketNo).Where(g => g.Count() > 1).Select(g => g.Key);
+            if (repeatPacketNos.Any())
+            {
+                // 若存在重复项则提示后返回
+                string repeatPacketNoText = string.Join("、", repeatPacketNos.ToArray());
+                MessageBox.Show("材料包号列存在以下重复值,请修改后保存:" + repeatPacketNoText, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+                return;
+            }
+
+            // 保存到数据库
+            if (CurrDeliveryReceipt.IsNewRow)
+            {
+                // 新增行插入到数据库
+                CurrDeliveryReceipt.IsNewRow = false;
+                foreach (var dtl in CurrDeliveryReceipt.DeliveryReceiptDetails)
+                {
+                    dtl.IsNewRow = false;
+                }
+                _service.Insert(CurrDeliveryReceipt);
+            }
+            else
+            {
+                // 已保存行更新到数据库
+                // todo 主表已保存,但子表明细 新增/删除/修改 怎么保存
+
+                _service.Update(CurrDeliveryReceipt);
+            }
+
+            // 提示保存结果
+            bool? isSuccess = _service.SaveChanges();
+            if (isSuccess == null || !(bool)isSuccess)
+            {
+                MessageBox.Show("数据库保存失败!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+            }
+            else
+            {
+                MessageBox.Show("保存成功。", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
+            }
+        }
+
+        [RelayCommand]
+        public void DetailSelectionChanged()
+        {
+
+        }
+
+        [RelayCommand]
+        public void AddDetail()
+        {
+            // 若当前主表行为空,则直接返回
+            if (CurrDeliveryReceipt == null) { return; }
+
+            // 新增一行子表
+            DeliveryReceiptDetail detail = new DeliveryReceiptDetail();
+            //DeliveryDetails.Add(detail);
+            // 当前子表数据绑定到主表 todo 主表行切换子表展示数据清空(但保存在主表中)
+            CurrDeliveryReceipt!.DeliveryReceiptDetails.Add(detail);
+        }
+
+        [RelayCommand]
+        public void DeleteDetail(DataGrid dataGridDeliveryDetail)
+        {
+            // todo 多选删除
+            // 主表行为空直接返回
+            if (CurrDeliveryReceipt == null) { return; }
+
+            // 已导出数据包的发货的不允许再新增明细
+            if (CurrDeliveryReceipt.IsReported)
+            {
+                MessageBox.Show("当前发货单已导出数据包,不允许新增明细!");
+                return;
+            }
+
+            // 已导出数据包的发货的不允许再删除明细
+            if (CurrDeliveryReceipt.IsReported)
+            {
+                MessageBox.Show("当前发货单已导出数据包,不允许删除明细!");
+                return;
+            }
+
+            // 若没有选择行,直接返回
+            DeliveryReceiptDetail? detail = dataGridDeliveryDetail.SelectedItem as DeliveryReceiptDetail;
+            if (detail == null) { return; }
+
+            // 未保存行直接删除
+            if (detail.IsNewRow)
+            {
+                CurrDeliveryReceipt.DeliveryReceiptDetails.Remove(detail);
+                dataGridDeliveryDetail.Items.Refresh(); // 刷新索引
+            }
+            // 已保存行 则询问
+            else
+            {
+                // 判断当前行是否已修改
+                EntityState? state = App.Current.Services.GetService<IDataBaseService<DeliveryReceiptDetail>>()?.Entry(detail);
+                if (state == null)
+                {
+                    MessageBox.Show("判断当前行状态出错!");
+                    return;
+                }
+                switch (state)
+                {
+                    case EntityState.Unchanged:
+                        {
+                            // 已保存行询问,不删除则跳过
+                            MessageBoxResult result = MessageBox.Show("是否确定删除当前行?", "询问", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
+                            if (result != MessageBoxResult.Yes) { return; }
+                            break;
+                        }
+                    //case EntityState.Added: // 不会是新增行(IsNewRow)
+                    //case EntityState.Deleted: // 正常情况下不会出现 Deleted (删除后刷新界面)
+                    //case EntityState.Detached: // 正常情况下不会出现 Detached
+                    case EntityState.Modified: // 询问
+                        {
+                            // 已修改行询问,不删除则跳过
+                            MessageBoxResult result = MessageBox.Show("当前行数据已修改,是否确定删除当前行?", "询问", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
+                            if (result != MessageBoxResult.Yes) { return; }
+                            break;
+                        }
+                    default: break;
+                }
+
+                // ×界面删除当前行
+                // 数据库删除当前行
+                // 刷新界面
+
+            }
+        }
+
+        [RelayCommand]
+        public void ImportDetail()
+        {
+            // 主表行为空直接返回
+            if (CurrDeliveryReceipt == null) { return; }
+
+            // 已导出数据包的发货的不允许再导入明细
+            if (CurrDeliveryReceipt.IsReported)
+            {
+                MessageBox.Show("当前发货单已导出数据包,不允许导入明细!");
+                return;
+            }
+
+            // 删除新增的空行
+            DeleteEmptyRows();
+
+            // 若子表中存在未保存的行,则不允许导入
+            var newRows = CurrDeliveryReceipt.DeliveryReceiptDetails.Where(x => x.IsNewRow);
+            if (newRows.Any())
+            {
+                MessageBox.Show("存在未保存的行,请保存后导入。");
+                return;
             }
 
-            var deliveryReceipts = _service.Query();
+            // 选择导入的文件
+            OpenFileDialog openFileDialog = new OpenFileDialog();
+            openFileDialog.Title = "选择导入文件的路径";
+            openFileDialog.Filter = "Excel 文件|*.xlsx;*.xls|Excel 工作簿|*.xlsx|Excel 97-2003工作簿|*.xls"; // 只能导入Excel
+            string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+            openFileDialog.InitialDirectory = desktopPath; // 默认从桌面获取文件
+            if (openFileDialog.ShowDialog() != true) { return; }
 
-            // 获取检验申请数据
-            var inspectApplies = App.Current.Services.GetService<IDataBaseService<InspectApply>>()?.Query().Include(x => x.InspectApplyContractDetails);
-            if (inspectApplies == null || !inspectApplies.Any())
+            // 导入 Excel
+            DataTable dtImport = ExcelUtil.ReadExcelFileToDataTable(openFileDialog.FileName);
+            if (dtImport == null || dtImport.Rows.Count == 0)
             {
-                MessageBox.Show("获取检验申请数据失败!");
+                MessageBox.Show("获取Excel数据失败,请重试!");
                 return;
             }
-            InspectApplies = new ObservableCollection<InspectApply>(inspectApplies);
+
+            // 检查 Excel 中是否存在重复项
+            var importRepeatPacketNos = dtImport.AsEnumerable().GroupBy(x => x["包号"]).Where(g => g.Count() > 1).Select(g => g.Key).ToList();
+            if (importRepeatPacketNos.Any())
+            {
+                string repeatNos = string.Join("\n", importRepeatPacketNos);
+                MessageBox.Show("Excel 中以下包号存在重复,请检查数据后重新导入:\n" + repeatNos, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
+                return;
+            }
+
+            // 导入 Excel 中的数据
+            string[] packetNos = CurrDeliveryReceipt.DeliveryReceiptDetails.Select(x => x.PacketNo.ToString()).ToArray();
+            StringBuilder importRepeatMsg = new StringBuilder();
+            StringBuilder importNullMsg = new StringBuilder();
+            int importCount = 0;
+            foreach (DataRow dr in dtImport.Rows)
+            {
+                // 跳过系统中已经存在的包号:提示用户 Excel 中哪几行的材料名称已存在,不导入
+                if (packetNos.Contains(dr["包号"].ToString()))
+                {
+                    // 拼接重复的材料名称
+                    importRepeatMsg.Append($"\n第 {dtImport.Rows.IndexOf(dr) + 1} 行、");
+                    continue;
+                }
+
+                // 跳过数量为空的行:提示用户 Excel 中哪几行的必录项为空,检查数据后重新导入
+                string? qtyStr = dr["数量"].ToString();
+                if (string.IsNullOrWhiteSpace(qtyStr) || qtyStr.Equals("0"))
+                {
+                    // 拼接行号
+                    importNullMsg.Append($"第 [{dtImport.Rows.IndexOf(dr) + 1}] 行、");
+                    continue;
+                }
+
+                // 从 DataRow → Material
+                DeliveryReceiptDetail detail = ConvertToEntity(dr);
+                CurrDeliveryReceipt.DeliveryReceiptDetails.Add(detail);
+                _service?.Update(CurrDeliveryReceipt);
+                importCount++;
+            }
+
+            // 拼接导入异常信息
+            if (importRepeatMsg.Length > 0)
+            {
+                importRepeatMsg = importRepeatMsg.Remove(importRepeatMsg.Length - 1, 1);
+                importRepeatMsg = importRepeatMsg.Insert(0, $"忽略包号已存在的行:").AppendLine();
+            }
+            if (importNullMsg.Length > 0)
+            {
+                importNullMsg = importNullMsg.Remove(importNullMsg.Length - 1, 1);
+                importNullMsg = importNullMsg.Insert(0, "忽略数量为空的行:").AppendLine();
+            }
+
+            if (importCount > 0)
+            {
+                bool? isSuccess = _service?.SaveChanges();
+                if (isSuccess == true)
+                {
+                    MessageBox.Show($"成功导入 {importCount} 行。\n" + importRepeatMsg + importNullMsg);
+                }
+                else
+                {
+                    MessageBox.Show($"导入失败!\n");
+                }
+            }
+            else
+            {
+                MessageBox.Show($"选择的文件中没有需要导入的行!\n" + importRepeatMsg + importNullMsg);
+            }
+        }
+
+        [RelayCommand]
+        public void ExportDetail()
+        {
+            // 自动清除新增的空行
+            DeleteEmptyRows();
+
+            // 若存在未保存的行,则不允许导出
+            var newRows = CurrDeliveryReceipt.DeliveryReceiptDetails.Where(x => x.IsNewRow);
+            if (newRows.Any())
+            {
+                MessageBox.Show("存在未保存的行,请保存后导出。");
+                return;
+            }
+
+            // 选择导出路径
+            SaveFileDialog saveFileDialog = new SaveFileDialog();
+            saveFileDialog.Title = "选择保存文件的路径";
+            saveFileDialog.Filter = "Excel 文件|*.xlsx;*.xls|Excel 工作簿|*.xlsx|Excel 97-2003工作簿|*.xls|所有文件|*.*";
+            string desktopPath = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
+            saveFileDialog.InitialDirectory = desktopPath; // 默认保存到桌面。若不设置,默认保存到 文档 路径下
+            string fileName = "发货单明细-" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx";
+            saveFileDialog.FileName = fileName; // 默认文件名
+            if (saveFileDialog.ShowDialog() != true) { return; }
+
+            // 创建 DataTable 
+            DataTable dtDetail = new DataTable();
+            dtDetail.Columns.Add("包号", typeof(string));
+            dtDetail.Columns.Add("数量", typeof(decimal));
+            foreach (var mate in CurrDeliveryReceipt.DeliveryReceiptDetails)
+            {
+                DataRow dr = dtDetail.NewRow();
+                dr["包号"] = mate.PacketNo;
+                dr["数量"] = mate.ShippedQuantity;
+                dtDetail.Rows.Add(dr);
+            }
+
+            // 导出 Excel
+            ExcelUtil.CreateExcelFromDataTable(dtDetail, saveFileDialog.FileName);
+        }
+
+        /// <summary>
+        /// 加载数据
+        /// </summary>
+        private void LoadData()
+        {
+            // 发货单主表数据源
+            var deliveryReceipts = _service.Query().Include(x => x.DeliveryReceiptDetails);
+            DeliveryReceipts = new ObservableCollection<DeliveryReceipt>(deliveryReceipts);
+
+            // 获取每一行发货单对应的检验合格数量和剩余数量
+            foreach (var delivery in DeliveryReceipts)
+            {
+                // 检验合格数量:检验申请主表-报检数量
+                string applyNo = delivery.ApplyNo;
+                InspectApply? apply = App.Current.Services.GetService<IDataBaseService<InspectApply>>()?.Query(x => x.ApplyNo.Equals(applyNo)).FirstOrDefault();
+                if (apply == null) { continue; }
+                delivery.InspQty = apply.InspQuantity;
+
+                // 可发运数量:检验申请主表-报检数量 - 已发运数量
+                string contractNo = delivery.ContractNo;
+                double hasShippedQty = (double)DeliveryReceipts.Where(x => x.ApplyNo.Equals(applyNo) && x.ContractNo.Equals(contractNo)).Select(x => x.ShippedQty).Sum();
+                delivery.RemainQty = delivery.InspQty - hasShippedQty;
+
+                // 是否为新行:否
+                delivery.IsNewRow = false;
+
+                // 子表明细行
+                var details = delivery.DeliveryReceiptDetails.OrderBy(x => x.PacketNo).ToArray();
+                foreach(var dtl in details)
+                {
+                    dtl.IsNewRow = false;
+                }
+                delivery.DeliveryReceiptDetails = new ObservableCollection<DeliveryReceiptDetail>(details);
+            }
+        }
+
+        /// <summary>
+        /// 删除子表的新增空行
+        /// </summary>
+        private void DeleteEmptyRows()
+        {
+            // 删除子表的新增空行
+            var emptyRows = CurrDeliveryReceipt.DeliveryReceiptDetails.Where(x => x.IsNewRow && string.IsNullOrEmpty(x.PacketNo) && x.ShippedQuantity == 0);
+            if (emptyRows.Any())
+            {
+                for (int i = emptyRows.Count() - 1; i >= 0; i--)
+                {
+                    CurrDeliveryReceipt.DeliveryReceiptDetails.Remove(emptyRows.ElementAt(i));
+                }
+            }
+        }
+
+        /// <summary>
+        /// 导入Excel的行转换为Entity
+        /// </summary>
+        private DeliveryReceiptDetail ConvertToEntity(DataRow dr)
+        {
+            DeliveryReceiptDetail detail = new DeliveryReceiptDetail();
+            //detail.Name = dr["材料名称*"].ToString() ?? "";
+            //detail.VarietyCode = dr["品种代码*"].ToString() ?? "";
+            //detail.CategoryCode = dr["材料类型代码*"].ToString() ?? "";
+            //detail.Specification = dr["材料规格*"].ToString() ?? "";
+            //detail.MeasureUnit = dr["计量单位*"].ToString() ?? "";
+            //detail.NormName = dr["材料规范名称"].ToString() ?? "";
+            //detail.Note = dr["备注"].ToString();
+            //detail.IsEnabled = dr["是否可用"].ToString()!.Equals("是") ? true : false;
+            //detail.IsNewRow = false;  // 直接保存
+
+            return detail;
+        }
+
+        /// <summary>
+        /// 判断行是否已修改
+        /// </summary>
+        private bool IsChanged(DeliveryReceipt deliveryReceipt)
+        {
+            // 新增行直接返回 true
+            if (deliveryReceipt.IsNewRow)
+            {
+                return true;
+            }
+
+            // 已保存的行检查数据库状态
+            var state = _service.Entry(deliveryReceipt);
+            if (state != EntityState.Unchanged)
+            {
+                return true;
+            }
+
+            // 检查子表行
+            foreach (var detail in deliveryReceipt.DeliveryReceiptDetails)
+            {
+                if (detail.IsNewRow)
+                {
+                    return true;
+                }
+
+                var detailState = App.Current.Services.GetService<IDataBaseService<DeliveryReceiptDetail>>()?.Entry(detail);
+                if (detailState != EntityState.Unchanged)
+                {
+                    return true;
+                }
+
+                // todo 如果子表删除了几行是什么情况?
+            }
+
+            return false;
         }
     }
 }

+ 2 - 2
UniformMaterialManagementSystem/ViewModels/MaterialCompanyViewModel.cs

@@ -175,7 +175,7 @@ namespace UniformMaterialManagementSystem.ViewModels
             }
 
             // 检查新增行的必录项是否为空
-            var nullCompanies = Companies.Where(x => x.IsNewRow && (string.IsNullOrEmpty(x.Name) || string.IsNullOrEmpty(x.ShortName) || string.IsNullOrEmpty(x.Code) || string.IsNullOrEmpty(x.SystemCode)));
+            var nullCompanies = Companies.Where(x => x.IsNewRow && (string.IsNullOrEmpty(x.Name) || string.IsNullOrEmpty(x.ShortName) || string.IsNullOrEmpty(x.Code))); // || string.IsNullOrEmpty(x.SystemCode)
             if (nullCompanies.Any())
             {
                 // 如果存在未填的必录项,则提示
@@ -193,7 +193,7 @@ namespace UniformMaterialManagementSystem.ViewModels
                     nullCaptions = string.IsNullOrEmpty(nullItem.Name) ? nullCaptions.Append("“材料企业名称”、") : nullCaptions;
                     nullCaptions = string.IsNullOrEmpty(nullItem.ShortName) ? nullCaptions.Append("“材料企业简称”、") : nullCaptions;
                     nullCaptions = string.IsNullOrEmpty(nullItem.Code) ? nullCaptions.Append("“材料企业代码”、") : nullCaptions;
-                    nullCaptions = string.IsNullOrEmpty(nullItem.SystemCode) ? nullCaptions.Append("“军网材料企业代码”、") : nullCaptions;
+                    //nullCaptions = string.IsNullOrEmpty(nullItem.SystemCode) ? nullCaptions.Append("“军网材料企业代码”、") : nullCaptions;
                     nullCaptions = nullCaptions.Remove(nullCaptions.Length - 1, 1);
                     nullMsg.Append($"\n第{rowIndex + 1}行的{nullCaptions}");
 

+ 117 - 0
UniformMaterialManagementSystem/ViewModels/SelectApplyContractDialogViewModel.cs

@@ -0,0 +1,117 @@
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using CommunityToolkit.Mvvm.ComponentModel;
+using CommunityToolkit.Mvvm.Input;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using UniformMaterialManagementSystem.Entities;
+using UniformMaterialManagementSystem.Services;
+
+namespace UniformMaterialManagementSystem.ViewModels
+{
+    public partial class SelectApplyContractDialogViewModel : ObservableObject
+    {
+
+
+        [ObservableProperty]
+        private ObservableCollection<InspectApply> _inspectApplies = new ObservableCollection<InspectApply>();
+
+        [ObservableProperty]
+        private ObservableCollection<InspectApplyContractDetail> _inspectApplyContracts = new ObservableCollection<InspectApplyContractDetail>();
+
+        public delegate void GetRetuenData(InspectApply inspectApply, InspectApplyContractDetail inspectApplyContractDetail);
+        public GetRetuenData getRetuenData = null!;
+
+
+        internal InspectApply? SelectedApply = null;
+
+        internal InspectApplyContractDetail? SelectedContract = null;
+
+        public SelectApplyContractDialogViewModel()
+        {
+            // 获取检验申请编号数据源
+            var inspectApplies = App.Current.Services.GetService<IDataBaseService<InspectApply>>()?.Query().Include(x => x.InspectApplyContractDetails);
+            if (inspectApplies == null || !inspectApplies.Any())
+            {
+                MessageBox.Show("获取检验申请数据失败!");
+                return;
+            }
+            InspectApplies = new ObservableCollection<InspectApply>(inspectApplies);
+
+        }
+
+        [RelayCommand]
+        public void Loaded(DataGrid fdgContract)
+        {
+            // todo 当窗口 SizeChanged 重新计算列宽
+            // todo 没有减去 FilterDataGrid 右侧竖向条的宽度,暂时不研究
+            // 根据当前窗口的宽度计算列宽
+            //double actualWidth = fdgContract.ActualWidth;
+            //double colWidth = actualWidth / 6;
+            //foreach (var col in fdgContract.Columns)
+            //{
+            //    if (col.Header.Equals("采购机构"))
+            //    {
+            //        col.Width = 2 * colWidth;
+            //    }
+            //    else
+            //    {
+            //        col.Width = colWidth;
+            //    }
+            //}
+        }
+
+        [RelayCommand]
+        public void ApplySelectionChanged(DataGrid fdgApply)
+        {
+            // 获取当前行的合同信息
+            InspectApply? currApply = fdgApply.SelectedItem as InspectApply;
+            if (currApply == null) { return; }
+
+            InspectApplyContracts.Clear();
+            // !不能直接赋值,否则清空后会把 currApply 的明细也清空
+            //InspectApplyContracts = currApply.InspectApplyContractDetails; 
+            InspectApplyContracts = new ObservableCollection<InspectApplyContractDetail>(currApply.InspectApplyContractDetails);
+        }
+
+        [RelayCommand]
+        public void Confirm(object parameters)
+        {
+            // 获取控件
+            object[]? values = parameters as object[];
+            if (values == null || values.Length != 3) { return; }
+            DataGrid? fdgApply = values[0] as DataGrid;
+            DataGrid? fdgContract = values[1] as DataGrid;
+            Window? window = values[2] as Window;
+            if (fdgApply == null || fdgContract == null || window == null) { return; }
+
+            // 检查是否勾选且只勾选一行
+            var selectedApply = fdgApply.SelectedItem as InspectApply;
+            var selectedContract = fdgContract.SelectedItem as InspectApplyContractDetail;
+            if (selectedApply == null || selectedContract == null)
+            {
+                MessageBox.Show("请选择一条检验申请和一条合同信息!");
+                return;
+            }
+
+            // 传递参数到父窗体
+            //getRetuenData(selectedApply, selectedContract);
+            this.SelectedApply = selectedApply;
+            this.SelectedContract = selectedContract;
+
+            window.Close();
+        }
+
+        [RelayCommand]
+        public void Cancel(Window window)
+        {
+            window.Close();
+        }
+    }
+}

+ 480 - 500
UniformMaterialManagementSystem/Views/DeliveryReceiptControl.xaml

@@ -5,8 +5,11 @@
              xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
              xmlns:local="clr-namespace:UniformMaterialManagementSystem.Views"
              xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:behavoirs="clr-namespace:UniformMaterialManagementSystem.Behaviors"
              xmlns:utils="clr-namespace:UniformMaterialManagementSystem.Utils"
              xmlns:converters="clr-namespace:UniformMaterialManagementSystem.Converters"
+             xmlns:fdg="http://FilterDataGrid.Control.com/2024"
+             xmlns:custom="clr-namespace:UniformMaterialManagementSystem.Custom"
              mc:Ignorable="d" 
              d:DesignHeight="450" d:DesignWidth="800">
     <UserControl.Resources>
@@ -34,26 +37,27 @@
 
         <!-- 列标题模板:必须在列标题样式引用之前定义 -->
         <ControlTemplate x:Key="CustomColumnHeaderTemplate" TargetType="DataGridColumnHeader">
-            <!-- 模板中给列标题最外层加一层边框:BorderThickness 分别设置 左 上 右 下 的边框宽度 -->
-            <Border BorderThickness="0 0 1 1"
-                    BorderBrush="Black">
+            <!-- BorderThickness 分别设置左,上,右,下的边框宽度。 -->
+            <Border BorderThickness="0, 0, 1, 1" BorderBrush="Black">
                 <Grid>
                     <Grid.ColumnDefinitions>
                         <ColumnDefinition />
                         <ColumnDefinition Width="Auto" />
                     </Grid.ColumnDefinitions>
-                    <ContentPresenter Grid.Column="0" 
-                                      HorizontalAlignment="Center" VerticalAlignment="Center" />
-                    <!-- 排序按钮 -->
+                    <!-- 重定义控件模板,在 Style 中设置的居中属性会被覆盖,所以在这里也需要设置居中 -->
+                    <ContentPresenter Grid.Column="0"
+                              HorizontalAlignment="Center" VerticalAlignment="Center" />
+                    <!-- 列排序图标 -->
                     <Path x:Name="SortArrow"
-                          Grid.Column="1"
-                          Margin="0,0,5,0"
-                          VerticalAlignment="Center"
-                          Data="M 0 0 L 10 0 L 5 5 Z"
-                          Fill="Gray"
-                          Visibility="Collapsed" />
+                  Grid.Column="1"
+                  Margin="0,0,5,0"
+                  VerticalAlignment="Center"
+                  Data="M 0 0 L 10 0 L 5 5 Z"
+                  Fill="Gray"
+                  Visibility="Collapsed" />
                 </Grid>
             </Border>
+            <!-- 列排序按钮 -->
             <ControlTemplate.Triggers>
                 <Trigger Property="SortDirection" Value="Ascending">
                     <Setter TargetName="SortArrow" Property="Data" Value="M 0 5 L 10 5 L 5 0 Z" />
@@ -126,6 +130,7 @@
             </Style.Triggers>
         </Style>
 
+        <!-- todo 为什么必须设置文本列样式之后才能居中,单元格设置不起作用? -->
         <!-- 单元格样式 -->
         <Style x:Key="CustomCellStyle" TargetType="DataGridCell"
                BasedOn="{StaticResource {x:Type DataGridCell}}">
@@ -149,17 +154,24 @@
             <Setter Property="TextAlignment" Value="Center" />
         </Style>
         
+        <!-- 文本列编辑状态居左 -->
+        <Style x:Key="TextColumnEditingStyle" TargetType="TextBox">
+            <Setter Property="HorizontalContentAlignment" Value="Left" />
+            <Setter Property="VerticalContentAlignment" Value="Center" />
+        </Style>
+
         <!-- 复选框列居中 -->
         <Style x:Key="CheckBoxColumnElementStyle" TargetType="CheckBox">
             <Setter Property="HorizontalAlignment" Value="Center" />
             <Setter Property="VerticalAlignment" Value="Center" />
+            <Setter Property="Padding" Value="0, 5, 0, 5" />
         </Style>
 
         <!-- TextBlock 一般用来展示文本,TextBox 一般用于输入文本 -->
         <!-- TextBlock 样式 -->
         <Style x:Key="CustomTextBlockStyle" TargetType="TextBlock">
             <Setter Property="Width" Value="100" />
-            <Setter Property="Height" Value="30" />
+            <Setter Property="Height" Value="20" />
             <Setter Property="FontSize" Value="14" />
             <Setter Property="VerticalAlignment" Value="Center" />
             <Setter Property="TextAlignment" Value="Right" />
@@ -167,7 +179,7 @@
         </Style>
 
         <!-- Label 比 TextBlock 更复杂,但可以方便的设置边框 -->
-        <!-- Label 样式 -->
+        <!-- Label 样式:有边框 -->
         <Style x:Key="CustomLabelStyle" TargetType="Label">
             <Setter Property="BorderBrush" Value="LightGray" />
             <Setter Property="BorderThickness" Value="1" />
@@ -178,7 +190,19 @@
             <Setter Property="FontSize" Value="14" />
             <Setter Property="HorizontalContentAlignment" Value="Center" />
             <Setter Property="VerticalContentAlignment" Value="Center" />
-            <Setter Property="Margin" Value="5" />
+            <Setter Property="Margin" Value="0 5 0 5" />
+        </Style>
+
+        <!-- TextBox :输入框 -->
+        <Style x:Key="CustomTextBoxStyle" TargetType="TextBox">
+            <Setter Property="BorderBrush" Value="Gray" />
+            <Setter Property="BorderThickness" Value="1" />
+            <Setter Property="Width" Value="200" />
+            <Setter Property="Height" Value="30" />
+            <Setter Property="FontSize" Value="14" />
+            <Setter Property="HorizontalContentAlignment" Value="Center" />
+            <Setter Property="VerticalContentAlignment" Value="Center" />
+            <Setter Property="Margin" Value="0 5 0 5" />
         </Style>
 
         <!-- 转换器 -->
@@ -189,501 +213,457 @@
     <Grid>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="*" />
-            <ColumnDefinition Width="auto" />
-            <ColumnDefinition Width="2*" />
+            <ColumnDefinition Width="*" />
         </Grid.ColumnDefinitions>
-
-        <!-- 检验申请列表 -->
-        <Grid Grid.Row="0" Grid.Column="0">
-            <Grid.RowDefinitions>
-                <RowDefinition Height="auto" />
-                <RowDefinition Height="*" />
-                <RowDefinition Height="*" />
-            </Grid.RowDefinitions>
-
-            <!-- todo 查询工具栏
-                  直接 DataGrid 每列都可以筛选,+ 三列选择筛选,删除关键字查询 -->
-            <Border Grid.Row="0"
-                    BorderBrush="Gray"
+        <Grid.RowDefinitions>
+            <RowDefinition Height="auto" />
+            <RowDefinition Height="auto" />
+            <RowDefinition Height="auto" />
+            <RowDefinition Height="auto" />
+            <RowDefinition Height="*" />
+        </Grid.RowDefinitions>
+
+        <!-- 发货单工具栏 -->
+        <Border x:Name="toolBarDelivery"
+                Grid.Row="0" 
+                Grid.Column="0"
+                Grid.ColumnSpan="2"
+                BorderBrush="Gray"
+                BorderThickness="1">
+            <ToolBar Background="White"
+                     ToolBarTray.IsLocked="True">
+                <Button Content="新增发货单"
+                        Tag="{x:Static utils:RegularFontUtil.Add_Square_32}"
+                        Template="{StaticResource CustomToolBarButtomTemplate}"
+                        Command="{Binding AddDeliveryCommand}" />
+                <Button Content="删除发货单"
+                        Tag="{x:Static utils:RegularFontUtil.Delete_32}"
+                        Template="{StaticResource CustomToolBarButtomTemplate}"
+                        Command="{Binding DeleteDeliveryCommand}"
+                        CommandParameter="{Binding ElementName=fdgDelivery}" />
+                <Separator />
+                <Button Content="保存" 
+                        Tag="{x:Static utils:RegularFontUtil.Save_32}"
+                        Template="{StaticResource CustomToolBarButtomTemplate}"
+                        Command="{Binding SaveCommand}" />
+                
+                <!--<WrapPanel Orientation="Horizontal">
+                    <StackPanel Orientation="Horizontal">
+                        <TextBlock Text="检验申请编号:" 
+                                   Width="90"
+                                   Style="{StaticResource CustomTextBlockStyle}"  />
+
+                        <custom:AutoCompleteBox x:Name="autoListBoxNo"
+                            Background="White"
+                            Width="275"
+                            Margin="0 2 0 2"
+                            ItemsSource="{Binding InspectApplyNos}" />
+                    </StackPanel>
+                    <StackPanel Orientation="Horizontal">
+                        <TextBlock Text="产品名称:" 
+                                   Width="90"
+                                   Style="{StaticResource CustomTextBlockStyle}"  />
+                        <custom:AutoCompleteBox x:Name="autoListBoxMate"
+                            Background="White"
+                            Width="275"
+                            Margin="0 2 0 2"
+                            ItemsSource="{Binding ProductNames}"
+                            SelectedItem="{Binding SelectedProductName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
+                    </StackPanel>
+
+                    <Button Content="查询"
+                            Margin="5 0 5 0"
+                            Tag="{x:Static utils:RegularFontUtil.Search_32}"
+                            Template="{StaticResource CustomToolBarButtomTemplate}"
+                            Command="{Binding QueryCommand}"
+                            CommandParameter="{Binding ElementName=autoListBoxNo}">
+                    </Button>
+                </WrapPanel>-->
+            </ToolBar>
+        </Border>
+
+        <!-- 发货单主表:主表不允许编辑,只能选择一行后编辑信息 -->
+        <!-- todo 控件的高度为空,不能这么绑定:
+                  MaxHeight="{Binding Path=Width,ElementName=dataGridDeliveryDetail}"
+              行标题:RowHeaderStyle="{StaticResource CustomRowHeaderStyle}" -->
+        <fdg:FilterDataGrid x:Name="fdgDelivery"
+                            Grid.Row="1" 
+                            Grid.Column="0"
+                            Grid.ColumnSpan="2"
+                            FilterLanguage="SimplifiedChinese"
+                            Background="White"
+                            AutoGenerateColumns="False"
+                            CanUserAddRows="False"
+                            behavoirs:DataGridBehavior.RowNumbers="True"
+                            SelectionMode="Single"
+                            SelectionUnit="FullRow"
+                            HeadersVisibility="All"
+                            ColumnHeaderStyle="{StaticResource CustomColumnHeaderStyle}"
+                            RowHeaderStyle="{StaticResource CustomRowHeaderStyle}"
+                            RowStyle="{StaticResource CustomRowStyle}"
+                            CellStyle="{StaticResource CustomCellStyle}"
+                            IsReadOnly="True"
+                            MinHeight="100"
+                            SelectedItem="{Binding CurrDeliveryReceipt, Mode=TwoWay}"
+                            ItemsSource="{Binding DeliveryReceipts, Mode=TwoWay}">
+
+            <!-- DataGrid 事件 -->
+            <b:Interaction.Triggers>
+                <!-- 选择行切换事件 -->
+                <b:EventTrigger EventName="SelectionChanged">
+                    <!-- 绑定命令 -->
+                    <!--<b:InvokeCommandAction Command="{Binding DeliverySelectionChangedCommand}"
+                                           CommandParameter="{Binding ElementName=fdgDelivery}" />-->
+
+                    <!-- 调用方法:传递事件参数 -->
+                    <!-- TargetObject="{Binding }" 表示目标对象是当前的数据上下文,事件被触发时调用数据上下文的指定方法;指定的方法不需要绑定命令,参数必须与事件的参数一致 -->
+                    <!--<b:CallMethodAction TargetObject="{Binding }" 
+                                        MethodName="DeliverySelectionChanged" />-->
+
+                </b:EventTrigger>
+            </b:Interaction.Triggers>
+
+            <!-- todo 为什么只能设置为绝对宽度,比例宽度列标题不显示? -->
+            <!-- todo 检查下数据库字段,添加检验申请和合同编号字段等 -->
+            <!-- 数据结构 -->
+            <fdg:FilterDataGrid.Columns>
+                <fdg:FilterDataGridTextColumn Header="检验申请编号" Width="300"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ApplyNo}" />
+                <fdg:FilterDataGridTextColumn Header="合同编号" Width="200"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ContractNo}" />
+                <fdg:FilterDataGridTextColumn Header="产品名称" Width="200"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ProductName}" />
+                <fdg:FilterDataGridTextColumn Header="生产企业" Width="200"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding CompanyName}" />
+                <fdg:FilterDataGridTextColumn Header="接收单位" Width="200"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ReceivedCompanyName}" />
+                <fdg:FilterDataGridTextColumn Header="批次号" Width="100"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding BatchNo}" />
+                <fdg:FilterDataGridTextColumn Header="发运包数" Width="100"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ShippedPackets}" />
+                <fdg:FilterDataGridTextColumn Header="发运数量" Width="100"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ShippedQty}" />
+                <fdg:FilterDataGridTextColumn Header="发运时间" Width="100"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ShippedDate, StringFormat='yyyy/MM/dd'}" />
+                <fdg:FilterDataGridTextColumn Header="发运承办人" Width="100"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ShippedMan}" />
+                <fdg:FilterDataGridTextColumn Header="联系电话" Width="100"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ShippedTel}" />
+            </fdg:FilterDataGrid.Columns>
+        </fdg:FilterDataGrid>
+
+        <!-- todo 拖动限制:左右拖动至少留出检验申请/发货单的一部分,上下拖动至少;留出主表列标题/子表工具栏+列标题 -->
+        <!-- 水平拖动分割线 -->
+        <GridSplitter Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Height="5"  HorizontalAlignment="Stretch" />
+
+        <!-- todo 设置 StackPanel 高度超出范围时自动拉伸 -->
+        <!-- 发货单信息 -->
+        <StackPanel x:Name="panelInfo"
+                    Grid.Row="3" 
+                    Grid.RowSpan="2"
+                    Grid.Column="0"
+                    Background="White"
+                    Orientation="Vertical"
+                    CanVerticallyScroll="True"
+                    VerticalAlignment="Stretch">
+
+            <!-- 表头信息 -->
+            <Border Grid.Row="0" Grid.Column="0"
+                    BorderBrush="Gray" 
                     BorderThickness="1">
-                <ToolBar Background="White"
-                         ToolBarTray.IsLocked="True">
-                    <TextBlock Text="产品名称:    " />
-                    <TextBlock Text="生产企业:    " />
-                    <TextBlock Text="批号:  " />
-                    <Button Content="btn查询"></Button>
-                    <TextBlock Text="关键字:" />
-                    <Button Content="btn关键字查询" />
-                </ToolBar>
+                <Grid Background="White">
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="auto" />
+                        <ColumnDefinition Width="*" />
+                    </Grid.ColumnDefinitions>
+
+                    <Label Grid.Column="0" 
+                           BorderBrush="Gray" BorderThickness="1 0 1 0" 
+                           Width="100"
+                           Content="表头信息" 
+                           FontWeight="Bold"
+                           VerticalAlignment="Stretch"
+                           HorizontalContentAlignment="Center"
+                           VerticalContentAlignment="Center"
+                           Padding="5" />
+
+                    <WrapPanel Grid.Column="1" 
+                                   Orientation="Horizontal">
+                        <!-- 产品名称 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="产品名称:" Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label x:Name="lblMaterial"
+                                   Content="{Binding CurrDeliveryReceipt.ProductName}" 
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+                        <!-- 生产企业 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="生产企业:" Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label Content="{Binding CurrDeliveryReceipt.CompanyName}" 
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+
+                    </WrapPanel>
+                </Grid>
+
             </Border>
 
-            <!-- 检验申请数据 -->
-            <DataGrid x:Name="dataGridApply"
-                      Grid.Row="1"
-                      Background="White"
-                      AutoGenerateColumns="False"
-                      CanUserAddRows="False"
-                      SelectionMode="Single"
-                      SelectionUnit="FullRow"
-                      ColumnHeaderStyle="{StaticResource CustomColumnHeaderStyle}"
-                      RowHeaderStyle="{StaticResource CustomRowHeaderStyle}"
-                      RowStyle="{StaticResource CustomRowStyle}"
-                      CellStyle="{StaticResource CustomCellStyle}"
-                      IsReadOnly="True"
-                      ItemsSource="{Binding InspectApplies, Mode=TwoWay}">
-                <!-- DataGrid 事件 -->
-                <b:Interaction.Triggers>
-                    <!-- 选择行切换事件 -->
-                    <b:EventTrigger EventName="SelectionChanged">
-                        <b:InvokeCommandAction Command="{Binding ApplySelectionChangedCommand}"
-                                           CommandParameter="{Binding ElementName=dataGridApply}" />
-                    </b:EventTrigger>
-                </b:Interaction.Triggers>
-
-                <!-- 数据结构 -->
-                <DataGrid.Columns>
-                    <DataGridTextColumn Header="检验申请编号" Width="300"
-                                        ElementStyle="{StaticResource TextColumnElementStyle}"
-                                        Binding="{Binding ApplyNo}" />
-                    <DataGridTextColumn Header="检验类别" Width="100"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding InspCategory}" />
-                    <DataGridTextColumn Header="产品名称" Width="300"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding ProductName}" />
-                    <DataGridTextColumn Header="生产企业" Width="300"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding Company}" />
-                    <DataGridTextColumn Header="报检数量" Width="100"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding InspQuantity}" />
-                    <DataGridTextColumn Header="生产开始日期" Width="100"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding StartProductDate, StringFormat='yyyy/MM/dd'}" />
-                    <DataGridTextColumn Header="生产结束日期" Width="100"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding EndProductDate, StringFormat='yyyy/MM/dd'}" />
-                    <DataGridTextColumn Header="申请检验时间" Width="100"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding InspDate, StringFormat='yyyy/MM/dd'}" />
-                    <DataGridCheckBoxColumn Header="检验报告生成状态" Width="100"
-                                            ElementStyle="{StaticResource CheckBoxColumnElementStyle}"
-                                            Binding="{Binding ReportStatus}" />
-                    <DataGridTextColumn Header="申请收到时间" Width="100"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding ApplyReceiveDate, StringFormat='yyyy/MM/dd'}" />
-                    <DataGridTextColumn Header="申请人" Width="100"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding ApplyUser}" />
-                    <DataGridTextColumn Header="工作年度" Width="100"
-                                    ElementStyle="{StaticResource TextColumnElementStyle}"
-                                    Binding="{Binding Year}" />
-                    <DataGridTextColumn Header="申请检验准备情况说明" Width="300"
-                                    Binding="{Binding ApplyDescription}">
-                        <!-- 文本默认不换行,但多行文本依然会自动加宽行高,设置行样式之后行高固定 -->
-                        <!--<DataGridTextColumn.ElementStyle>
-                        <Style>
-                            <Setter Property="TextBlock.TextWrapping" Value="NoWrap" />
-                        </Style>
-                    </DataGridTextColumn.ElementStyle>-->
-                    </DataGridTextColumn>
-                </DataGrid.Columns>
-
-            </DataGrid>
-
-            <!-- 检验申请 - 合同信息 -->
-            <DataGrid x:Name="dataGridContract"
-                      Grid.Row="2"
-                      Background="White"
-                      AutoGenerateColumns="False"
-                      CanUserAddRows="False"
-                      SelectionMode="Single"
-                      SelectionUnit="FullRow"
-                      ColumnHeaderStyle="{StaticResource CustomColumnHeaderStyle}"
-                      RowHeaderStyle="{StaticResource CustomRowHeaderStyle}"
-                      RowStyle="{StaticResource CustomRowStyle}"
-                      CellStyle="{StaticResource CustomCellStyle}"
-                      IsReadOnly="True"
-                      ItemsSource="{Binding InspectApplyContracts, Mode=TwoWay}">
-                <!-- DataGrid 事件 -->
-                <b:Interaction.Triggers>
-                    <!-- 选择行切换事件 -->
-                    <b:EventTrigger EventName="SelectionChanged">
-                        <b:InvokeCommandAction Command="{Binding ContractSelectionChangedCommand}"
-                           CommandParameter="{Binding ElementName=dataGridContract}" />
-                    </b:EventTrigger>
-                </b:Interaction.Triggers> 
-
-                <!-- 数据结构 -->
-                <DataGrid.Columns>
-                    <DataGridTextColumn Header="采购机构" Width="200"
-                                        ElementStyle="{StaticResource TextColumnElementStyle}"
-                                        Binding="{Binding PurchaseCompany}" />
-                    <DataGridTextColumn Header="合同编号" Width="100"
-                                        ElementStyle="{StaticResource TextColumnElementStyle}"
-                                        Binding="{Binding ContractNo}" />
-                    <!-- todo 字段是 string 类型的? -->
-                    <DataGridTextColumn Header="合同签订时间" Width="100"
-                                        ElementStyle="{StaticResource TextColumnElementStyle}"
-                                        Binding="{Binding SigningDate, StringFormat='yyyy/MM/dd'}" />
-                    <DataGridTextColumn Header="合同数量" Width="100"
-                                        ElementStyle="{StaticResource TextColumnElementStyle}"
-                                        Binding="{Binding InspectQty}" />
-                    <DataGridTextColumn Header="可报检数量" Width="100"
-                                        ElementStyle="{StaticResource TextColumnElementStyle}"
-                                        Binding="{Binding UnInspectQty}" />
-                </DataGrid.Columns>
-
-            </DataGrid>
-
-        </Grid>
-
-        <!-- 垂直拖动分割线 -->
-        <GridSplitter Grid.Column="1" Grid.Row="0" Width="5" HorizontalAlignment="Stretch"/>
-
-        <!-- 发货单页面 -->
-        <Grid Grid.Row="0" Grid.Column="4">
-            <Grid.ColumnDefinitions>
-                <ColumnDefinition Width="*" />
-                <ColumnDefinition Width="*" />
-            </Grid.ColumnDefinitions>
-            <Grid.RowDefinitions>
-                <RowDefinition Height="auto" />
-                <RowDefinition Height="auto" />
-                <RowDefinition Height="auto" />
-                <RowDefinition Height="auto" />
-                <RowDefinition Height="*" />
-            </Grid.RowDefinitions>
-
-            <!-- 发货单工具栏 -->
-            <Border x:Name="toolBarDelivery"
-                    Grid.Row="0" 
-                    Grid.Column="0"
-                    Grid.ColumnSpan="2"
-                    BorderBrush="Gray"
+            <!-- 合同信息 -->
+            <Border Grid.Row="1" Grid.Column="0"
+                    BorderBrush="Gray" 
                     BorderThickness="1">
-                <ToolBar Background="White"
-                         ToolBarTray.IsLocked="True">
-                    <Button Content="新增发货单"
-                            Tag="{x:Static utils:RegularFontUtil.Add_Circle_32}"
-                            Template="{StaticResource CustomToolBarButtomTemplate}"
-                            Command="{Binding AddDeliveryCommand}">
-                        <Button.CommandParameter>
-                            <MultiBinding Converter="{StaticResource MultiParamConverter}">
-                                <Binding ElementName="dataGridContract" />
-                                <Binding ElementName="dataGridDelivery" />
-                            </MultiBinding>
-                        </Button.CommandParameter>
-                    </Button>
-                    <Button Content="删除发货单"
-                            Tag="{x:Static utils:RegularFontUtil.Delete_32}"
-                            Template="{StaticResource CustomToolBarButtomTemplate}" />
-                    <Separator />
-                    <Button Content="保存" 
-                            Tag="{x:Static utils:RegularFontUtil.Save_32}"
-                            Template="{StaticResource CustomToolBarButtomTemplate}" />
-                    <Separator />
-                    <Button Content="导出回执单"
-                            Tag="{x:Static utils:RegularFontUtil.Arrow_Square_Down_24}"
-                            Template="{StaticResource CustomToolBarButtomTemplate}" />
-
-                </ToolBar>
+                <Grid Background="White">
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="auto" />
+                        <ColumnDefinition Width="*" />
+                    </Grid.ColumnDefinitions>
+
+                    <Label Grid.Column="0" 
+                           BorderBrush="Gray" 
+                           BorderThickness="1 0 1 0" 
+                           Width="100"
+                           Content="合同信息"
+                           FontWeight="Bold"
+                           VerticalAlignment="Stretch"
+                           HorizontalContentAlignment="Center"
+                           VerticalContentAlignment="Center" />
+
+                    <WrapPanel Grid.Column="1" 
+                               Orientation="Horizontal">
+                        <!-- 合同编号 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="合同编号:"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label Content="{Binding CurrDeliveryReceipt.ContractNo}"
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+                        <!-- 采购机构 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="采购机构:"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label Content="{Binding CurrDeliveryReceipt.PurchaseCompanyName}"
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+                        <!-- 合同数量 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="合同数量:"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label Content="{Binding CurrDeliveryReceipt.ContractQty}"
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+                        <!-- todo 日期格式设置不生效 -->
+                        <!-- 合同时间 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="合同时间:"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label Content="{Binding CurrDeliveryReceipt.ContractSigningDate, StringFormat='yyyy:MM:dd'}"
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+                        <!-- 检验合格数量、剩余数量 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="检验合格数量:"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <!-- 非保存列;重复设置 Width、TextAligment 属性,直接设置的值覆盖 Style 的值 -->
+                            <TextBlock Text="{Binding CurrDeliveryReceipt.InspQty}" 
+                                       Width="50"
+                                       Foreground="Red"
+                                       TextAlignment="Left"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <TextBlock Text="可发运数量:"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <TextBlock Text="{Binding CurrDeliveryReceipt.RemainQty}" 
+                                       Width="50"
+                                       Foreground="Red"
+                                       TextAlignment="Left"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                        </StackPanel>
+
+                    </WrapPanel>
+                </Grid>
+
             </Border>
 
-            <!-- 发货单主表 -->
-            <DataGrid x:Name="dataGridDelivery"
-                      Grid.Row="1" 
-                      Grid.Column="0"
-                      Grid.ColumnSpan="2"
-                      Background="White"
-                      AutoGenerateColumns="False"
-                      CanUserAddRows="False"
-                      SelectionMode="Single"
-                      SelectionUnit="FullRow"
-                      ColumnHeaderStyle="{StaticResource CustomColumnHeaderStyle}"
-                      RowHeaderStyle="{StaticResource CustomRowHeaderStyle}"
-                      RowStyle="{StaticResource CustomRowStyle}"
-                      CellStyle="{StaticResource CustomCellStyle}"
-                      IsReadOnly="True"
-                      MinHeight="100"
-                      MaxHeight="{Binding Path=Width,ElementName=dataGridApply}"
-                      ItemsSource="{Binding DeliveryReceipts, Mode=TwoWay}">
-                <!-- 数据结构 -->
-                <DataGrid.Columns>
-                    <DataGridTextColumn Header="产品名称" Width="2*"
-                                        Binding="{Binding MaterialName}" />
-                    <DataGridTextColumn Header="生产企业" Width="2*"
-                                        Binding="{Binding CompanyName}" />
-                    <DataGridTextColumn Header="接收单位" Width="2*"
-                                        Binding="{Binding ReceivedCompanyName}" />
-                    <DataGridTextColumn Header="批次号" Width="*"
-                                        Binding="{Binding BatchNo}" />
-                    <DataGridTextColumn Header="发运包数" Width="*"
-                                        Binding="{Binding ShippedPackets}" />
-                    <DataGridTextColumn Header="发运数量" Width="*"
-                                        Binding="{Binding ShippedQty}" />
-                    <DataGridTextColumn Header="发运时间" Width="*"
-                                        Binding="{Binding ShippedDate,StringFormat='yyyy/MM/dd'}" />
-                    <DataGridTextColumn Header="发运承办人" Width="*"
-                                        Binding="{Binding ShippedMan}" />
-                    <DataGridTextColumn Header="联系电话" Width="*"
-                                        Binding="{Binding ShippedTel}" />
-                </DataGrid.Columns>
-            </DataGrid>
-
-            <!-- todo 拖动限制:左右拖动至少留出检验申请/发货单的一部分,上下拖动至少;留出主表列标题/子表工具栏+列标题 -->
-            <!-- 水平拖动分割线 -->
-            <GridSplitter Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Height="5"  HorizontalAlignment="Stretch" />
-
-            <!-- todo 设置 StackPanel 高度超出范围时自动拉伸 -->
-            <!-- 发货单信息
-                        DataContext="{Binding CurrDeliveryReceipt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"       -->
-            <StackPanel x:Name="panelInfo"
-                        Grid.Row="3" 
-                        Grid.Column="0"
-                        Grid.RowSpan="2"
-                        Background="White"
-                        Orientation="Vertical"
-                        CanVerticallyScroll="True"
-                        VerticalAlignment="Stretch">
-
-                <!-- 表头信息 -->
-                <Border Grid.Row="0" BorderBrush="Gray" BorderThickness="1">
-                    <Grid Background="White">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="auto" />
-                            <ColumnDefinition Width="*" />
-                        </Grid.ColumnDefinitions>
-
-                        <Label Grid.Column="0" 
-                               BorderBrush="Gray" BorderThickness="1 0 1 0" 
-                               Content="表头信息" 
-                               FontWeight="Bold"
-                               VerticalAlignment="Stretch"
-                               VerticalContentAlignment="Center" />
-
-                        <WrapPanel Grid.Column="1" 
-                                   Orientation="Horizontal">
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="产品名称:" 
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label x:Name="lblMaterial"
-                                       Content="{Binding CurrDeliveryReceipt.MaterialName}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="生产企业:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label Content="{Binding CurrDeliveryReceipt.CompanyName}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-
-                        </WrapPanel>
-                    </Grid>
-
-                </Border>
-
-                <!-- 合同信息 -->
-                <Border Grid.Row="1"  
-                        BorderBrush="Gray" BorderThickness="1">
-                    <Grid Background="White">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="auto" />
-                            <ColumnDefinition Width="*" />
-                        </Grid.ColumnDefinitions>
-
-                        <Label Grid.Column="0" 
-                               BorderBrush="Gray" BorderThickness="1 0 1 0" 
-                               Content="合同信息"
-                               FontWeight="Bold"
-                               VerticalAlignment="Stretch"
-                               VerticalContentAlignment="Center" />
-
-                        <WrapPanel Grid.Column="1" 
-                                   Orientation="Horizontal">
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="采购机构:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label Content="{Binding CurrDeliveryReceipt.ProductCompanyName}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="合同编号:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label Content="{Binding CurrDeliveryReceipt.ContractNo}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="合同数量:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label Content="{Binding CurrDeliveryReceipt.ContractQty}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-                            <!-- 重复设置 Width、TextAligment 属性,直接设置的值覆盖 Style 的值 -->
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="检验合格数量:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <TextBlock Text="1000" 
-                                           Width="50"
-                                           Foreground="Red"
-                                           TextAlignment="Left"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <!-- todo 不包含本次 -->
-                                <TextBlock Text="剩余数量:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <TextBlock Text="800" 
-                                           Width="50"
-                                           Foreground="Red"
-                                           TextAlignment="Left"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="合同时间:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label Content="{Binding CurrDeliveryReceipt.ContractSigningDate}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-
-                        </WrapPanel>
-                    </Grid>
-
-                </Border>
-
-                <!-- 发运信息 -->
-                <Border Grid.Row="2"  
-                        BorderBrush="Gray" 
-                        BorderThickness="1">
-                    <Grid Background="White">
-                        <Grid.ColumnDefinitions>
-                            <ColumnDefinition Width="auto" />
-                            <ColumnDefinition Width="*" />
-                        </Grid.ColumnDefinitions>
-
-                        <Label Grid.Column="0"
-                               BorderBrush="Gray" BorderThickness="1 0 1 0" 
-                               Content="发运信息"
-                               FontWeight="Bold"
-                               VerticalAlignment="Stretch"
-                               VerticalContentAlignment="Center" />
-
-                        <WrapPanel Grid.Column="1"
+            <!-- 发运信息 -->
+            <Border Grid.Row="3" Grid.Column="0" Grid.RowSpan="2"  
+                    BorderBrush="Gray" 
+                    BorderThickness="1">
+                <Grid Background="White">
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition Width="auto" />
+                        <ColumnDefinition Width="*" />
+                    </Grid.ColumnDefinitions>
+
+                    <Label Grid.Column="0"
+                           BorderBrush="Gray" BorderThickness="1 0 1 0" 
+                           Width="100"
+                           Content="发运信息"
+                           FontWeight="Bold"
+                           VerticalAlignment="Stretch"
+                           HorizontalContentAlignment="Center"
+                           VerticalContentAlignment="Center" />
+
+                    <WrapPanel Grid.Column="1"
                                    Orientation="Horizontal">
-                            <!-- todo FontSize 默认 12? -->
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="接收单位:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label Content="{Binding CurrDeliveryReceipt.ReceivedCompanyName}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="批次号:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label Content="{Binding CurrDeliveryReceipt.BatchNo}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="发运包数:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label Content="{Binding CurrDeliveryReceipt.ShippedPackets}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="发运数量:"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <Label Content="{Binding CurrDeliveryReceipt.ShippedQty}" 
-                                       Style="{StaticResource CustomLabelStyle}" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="*发运时间:"
-                                           Foreground="Black"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <!--<Label Content="{Binding CurrDeliveryReceipt.ShippedDate}" 
-                                       Style="{StaticResource CustomLabelStyle}" />-->
-                                <DatePicker Width="200" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="发运承办人:"
-                                           Foreground="Black"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <TextBox Width="200" 
-                                         Height="30"
-                                         FontSize="14"
-                                         HorizontalContentAlignment="Center"
-                                         VerticalContentAlignment="Center"
-                                         BorderBrush="Gray"
-                                         BorderThickness="1"
-                                         Margin="10"
-                                         Text="{Binding CurrDeliveryReceipt.ShippedMan, Mode=TwoWay}" />
-                            </StackPanel>
-                            <StackPanel Orientation="Horizontal">
-                                <TextBlock Text="联系电话:"
-                                           Foreground="Black"
-                                           Style="{StaticResource CustomTextBlockStyle}" />
-                                <TextBox Width="200" 
-                                         Height="30"
-                                         FontSize="14"
-                                         HorizontalContentAlignment="Center"
-                                         VerticalContentAlignment="Center"
-                                         BorderBrush="Gray"
-                                         BorderThickness="1"
-                                         Margin="10"
-                                         Text="{Binding CurrDeliveryReceipt.ShippedTel, Mode=TwoWay}" />
-                            </StackPanel>
-
-                        </WrapPanel>
-                    </Grid>
-
-                </Border>
-
-            </StackPanel>
-
-            <!-- 发货单明细工具栏 -->
-            <Border Grid.Row="3" 
-                    Grid.Column="1"
-                    BorderBrush="Gray"
-                    BorderThickness="1"
-                    Height="{Binding Path=ActualHeight,ElementName=toolBarDelivery}">
-                <ToolBar Background="White"
-                         ToolBarTray.IsLocked="True">
-                    <Button Content="新增"
-                            Tag="{x:Static utils:RegularFontUtil.Add_Circle_32}"
-                            Template="{StaticResource CustomToolBarButtomTemplate}" />
-                    <Button Content="删除"
-                            Tag="{x:Static utils:RegularFontUtil.Delete_32}"
-                            Template="{StaticResource CustomToolBarButtomTemplate}" />
-                    <Separator />
-                    <Button Content="导入"
-                            Tag="{x:Static utils:RegularFontUtil.Arrow_Turn_Right_Down_48}"
-                            Template="{StaticResource CustomToolBarButtomTemplate}" />
-                    <Button Content="导出"
-                            Tag="{x:Static utils:RegularFontUtil.Arrow_Turn_Right_48}"
-                            Template="{StaticResource CustomToolBarButtomTemplate}" />
-                </ToolBar>
+                        <!-- todo FontSize 默认 12? -->
+                        <!-- 接收单位 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="接收单位:"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label Content="{Binding CurrDeliveryReceipt.ReceivedCompanyName}"
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+                        <!-- 批次号 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="批次号:" 
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label Content="{Binding CurrDeliveryReceipt.BatchNo}"
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+                        <!-- 发运包数 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="发运包数:"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label Content="{Binding CurrDeliveryReceipt.ShippedPackets}"
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+                        <!-- 发运数量 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="发运数量:"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <Label Content="{Binding CurrDeliveryReceipt.ShippedQty}"
+                                   Style="{StaticResource CustomLabelStyle}" />
+                        </StackPanel>
+                        <!-- todo 设置日期显示格式(需要修改Template,暂时不修改)减少日期选择框的 Padding(内部间距),选择按钮居右 -->
+                        <!-- 发运时间 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="*发运时间:"
+                                       Foreground="Black"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <DatePicker Width="200" 
+                                        Text="{Binding CurrDeliveryReceipt.ShippedDate, Mode=TwoWay}"
+                                        SelectedDateFormat="Short"
+                                        HorizontalContentAlignment="Center"
+                                        VerticalContentAlignment="Center"
+                                        Height="{Binding ElementName=lblMaterial, Path=Height}"
+                                        FontSize="14" />
+                        </StackPanel>
+                        <!-- 发运承办人 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="发运承办人:"
+                                       Foreground="Black"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <TextBox Text="{Binding CurrDeliveryReceipt.ShippedMan, Mode=TwoWay}"
+                                     Style="{StaticResource CustomTextBoxStyle}" />
+                        </StackPanel>
+                        <!-- 联系电话 -->
+                        <StackPanel Orientation="Horizontal">
+                            <TextBlock Text="联系电话:"
+                                       Foreground="Black"
+                                       Style="{StaticResource CustomTextBlockStyle}" />
+                            <TextBox Text="{Binding CurrDeliveryReceipt.ShippedTel, Mode=TwoWay}"
+                                     Style="{StaticResource CustomTextBoxStyle}" />
+                        </StackPanel>
+
+                    </WrapPanel>
+                </Grid>
+
             </Border>
 
-            <!-- 底部显示合计行? -->
-            <DataGrid x:Name="dataGridDeliveryDetail"
-                      Grid.Row="4" 
-                      Grid.Column="1"
-                      Background="White"
-                      AutoGenerateColumns="False"
-                      CanUserAddRows="False"
-                      SelectionMode="Single"
-                      SelectionUnit="CellOrRowHeader"
-                      ColumnHeaderStyle="{StaticResource CustomColumnHeaderStyle}"
-                      RowHeaderStyle="{StaticResource CustomRowHeaderStyle}"
-                      RowStyle="{StaticResource CustomRowStyle}"
-                      CellStyle="{StaticResource CustomCellStyle}"
-                      ItemsSource="{Binding DeliveryDetails, Mode=TwoWay}">
-                <!-- 数据结构 -->
-                <DataGrid.Columns>
-                    <DataGridTextColumn Header="序号" Width="*" />
-                    <DataGridTextColumn Header="材料包号" Width="*" />
-                    <DataGridTextColumn Header="数量" Width="*" />
-                </DataGrid.Columns>
-            </DataGrid>
-        </Grid>
-        
+        </StackPanel>
+
+        <!-- 发货单明细工具栏 -->
+        <Border Grid.Row="3" 
+                Grid.Column="1" 
+                BorderBrush="Gray"
+                BorderThickness="1"
+                Height="{Binding Path=ActualHeight,ElementName=toolBarDelivery}">
+            <ToolBar Background="White"
+                         ToolBarTray.IsLocked="True">
+                <Button Content="新增"
+                        Tag="{x:Static utils:RegularFontUtil.Add_Circle_32}"
+                        Template="{StaticResource CustomToolBarButtomTemplate}"
+                        Command="{Binding AddDetailCommand}" />
+                <Button Content="删除"
+                        Tag="{x:Static utils:RegularFontUtil.Delete_32}"
+                        Template="{StaticResource CustomToolBarButtomTemplate}"
+                        Command="{Binding DeleteDetailCommand}"
+                        CommandParameter="{Binding ElementName=dataGridDeliveryDetail}" />
+                <Separator />
+                <!-- Arrow_Turn_Right_48 -->
+                <Button Content="导入"
+                        Tag="{x:Static utils:RegularFontUtil.Arrow_Circle_Down_32}"
+                        Template="{StaticResource CustomToolBarButtomTemplate}"
+                        Command="{Binding ImportDetailCommand}" />
+                <Button Content="导出"
+                        Tag="{x:Static utils:RegularFontUtil.Arrow_Circle_Up_32}"
+                        Template="{StaticResource CustomToolBarButtomTemplate}"
+                        Command="{Binding ExportDetailCommand}" />
+            </ToolBar>
+        </Border>
+
+        <!-- todo 没有行的时候左边不显示行标题,必须有行之后才会出现行标题,列宽会变化 -->
+        <!-- todo 底部显示合计行?不用筛选,排序即可 -->
+        <fdg:FilterDataGrid x:Name="dataGridDeliveryDetail"
+                  Grid.Row="4" 
+                  Grid.Column="1" 
+                  Background="White"
+                  AutoGenerateColumns="False"
+                  CanUserAddRows="False"
+                  SelectionMode="Single"
+                  SelectionUnit="FullRow"
+                  ColumnHeaderStyle="{StaticResource CustomColumnHeaderStyle}"
+                  RowHeaderStyle="{StaticResource CustomRowHeaderStyle}"
+                  RowStyle="{StaticResource CustomRowStyle}"
+                  CellStyle="{StaticResource CustomCellStyle}"
+                  ItemsSource="{Binding CurrDeliveryReceipt.DeliveryReceiptDetails, Mode=TwoWay}">
+
+            <!-- DataGrid 事件 -->
+            <b:Interaction.Triggers>
+                <!-- 选择行切换事件 -->
+                <b:EventTrigger EventName="SelectionChanged">
+                    <b:InvokeCommandAction Command="{Binding DetailSelectionChangedCommand}" />
+                </b:EventTrigger>
+
+            </b:Interaction.Triggers>
+            
+            <!-- 数据结构 -->
+            <fdg:FilterDataGrid.Columns>
+                <!--<DataGridTextColumn Header="序号" Width="*" />-->
+                <fdg:FilterDataGridCheckBoxColumn Header="  " Width="50"
+                                                  ElementStyle="{StaticResource CheckBoxColumnElementStyle}"
+                                                  EditingElementStyle="{StaticResource CheckBoxColumnElementStyle}" />
+                <fdg:FilterDataGridTextColumn Header="材料包号" Width="*"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    EditingElementStyle="{StaticResource TextColumnEditingStyle}"
+                                    Binding="{Binding PacketNo, UpdateSourceTrigger=PropertyChanged}" />
+                <fdg:FilterDataGridTextColumn Header="发运数量" Width="*"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    EditingElementStyle="{StaticResource TextColumnEditingStyle}"
+                                    Binding="{Binding ShippedQuantity, UpdateSourceTrigger=PropertyChanged}" />
+            </fdg:FilterDataGrid.Columns>
+        </fdg:FilterDataGrid>
     </Grid>
 
 </UserControl>

+ 99 - 2
UniformMaterialManagementSystem/Views/DeliveryReceiptControl.xaml.cs

@@ -12,8 +12,10 @@ using System.Windows.Media;
 using System.Windows.Media.Imaging;
 using System.Windows.Navigation;
 using System.Windows.Shapes;
+using Microsoft.EntityFrameworkCore;
 using Microsoft.Extensions.DependencyInjection;
 using UniformMaterialManagementSystem.Entities;
+using UniformMaterialManagementSystem.Services;
 using UniformMaterialManagementSystem.ViewModels;
 
 namespace UniformMaterialManagementSystem.Views
@@ -31,8 +33,12 @@ namespace UniformMaterialManagementSystem.Views
             this.DataContext = App.Current.Services.GetService<DeliveryReceiptViewModel>();
 
             // 注册控件事件
-            this.dataGridApply.LoadingRow += DataGrid_LoadingRow;
-            this.dataGridContract.LoadingRow += DataGrid_LoadingRow;
+            // this.dataGridApply.LoadingRow += DataGrid_LoadingRow;
+            // this.dataGridContract.LoadingRow += DataGrid_LoadingRow;
+            this.fdgDelivery.LoadingRow += DataGrid_LoadingRow;
+            this.dataGridDeliveryDetail.LoadingRow += DataGrid_LoadingRow;
+
+            this.fdgDelivery.SelectionChanged += fdgDelivery_SelectionChanged;
         }
 
         private void DataGrid_LoadingRow(object? sender, DataGridRowEventArgs e)
@@ -40,5 +46,96 @@ namespace UniformMaterialManagementSystem.Views
             // 设置行标题:行索引+1
             e.Row.Header = e.Row.GetIndex() + 1;
         }
+
+        private void fdgDelivery_SelectionChanged(object sender, SelectionChangedEventArgs e)
+        {
+            DataGrid? fdgDelivery = sender as DataGrid;
+            if (fdgDelivery == null) { return; }
+
+            // 暂时屏蔽行切换事件
+            fdgDelivery.SelectionChanged -= fdgDelivery_SelectionChanged;
+
+            // 离开行之前,判断当前行是否未保存
+            if (e.RemovedItems.Count > 0)
+            {
+                DeliveryReceipt? leavingDelivery = e.RemovedItems[0] as DeliveryReceipt;
+                if (leavingDelivery == null) { return; }
+                // 手动切换为切换前的行(为了主表详情界面的值不切换)
+                fdgDelivery.SelectedItem = leavingDelivery;
+
+                // 判断当前行是否已修改
+                bool isChanged = IsChanged(leavingDelivery);
+                if (isChanged)
+                {
+                    // 询问是否切换
+                    MessageBoxResult res = MessageBox.Show("当前行已修改,切换行会丢失已修改的数据,是否确认切换行?", "询问", MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
+                    // 不切换:需要重新赋值
+                    if (res != MessageBoxResult.Yes)
+                    {
+                        // 取消切换行:当前行不变
+                        fdgDelivery.SelectedItem = leavingDelivery; // 需要重新赋值为旧行;CurrDeliveryReceipt 通过绑定自动更新
+                        fdgDelivery.Items.Refresh(); // 必须刷新界面
+
+                        fdgDelivery.SelectionChanged += fdgDelivery_SelectionChanged;
+                        return;
+                    }
+                    // 切换:继续执行,到 展示新行数据的部分
+                }
+            }
+
+            // 进入新的行时,界面展示新行数据
+            if (e.AddedItems.Count > 0)
+            {
+                DeliveryReceipt? newDelivery = e.AddedItems[0] as DeliveryReceipt;
+                if (newDelivery == null) { return; }
+                fdgDelivery.SelectedItem = newDelivery ?? null; // 手动切换为null,所以需要重新赋值
+                                                                //fdgDelivery.Items.Refresh(); // 不取消切换不用刷新?
+
+                // 根据当前行设置子表明细
+                this.dataGridDeliveryDetail.ItemsSource = newDelivery.DeliveryReceiptDetails;
+            }
+
+            
+
+            // 重新响应事件
+            fdgDelivery.SelectionChanged += fdgDelivery_SelectionChanged;
+        }
+        /// <summary>
+        /// 判断行是否已修改
+        /// </summary>
+        private bool IsChanged(DeliveryReceipt deliveryReceipt)
+        {
+            // 新增行直接返回 true
+            if (deliveryReceipt.IsNewRow)
+            {
+                return true;
+            }
+
+            // 已保存的行检查数据库状态
+            var state = App.Current.Services.GetService<IDataBaseService<DeliveryReceipt>>()?.Entry(deliveryReceipt);
+            if (state != EntityState.Unchanged)
+            {
+                return true;
+            }
+
+            // 检查子表行
+            foreach (var detail in deliveryReceipt.DeliveryReceiptDetails)
+            {
+                if (detail.IsNewRow)
+                {
+                    return true;
+                }
+
+                var detailState = App.Current.Services.GetService<IDataBaseService<DeliveryReceiptDetail>>()?.Entry(detail);
+                if (detailState != EntityState.Unchanged)
+                {
+                    return true;
+                }
+
+                // todo 如果子表删除了几行是什么情况?
+            }
+
+            return false;
+        }
     }
 }

+ 1 - 1
UniformMaterialManagementSystem/Views/MaterialCompany.xaml

@@ -274,7 +274,7 @@
                                     EditingElementStyle="{StaticResource TextColumnEditingElementStyle}"
                                     ElementStyle="{StaticResource TextColumnElementStyle}"
                                     Binding="{Binding Code, UpdateSourceTrigger=PropertyChanged}" />
-                <DataGridTextColumn Header="军网材料企业代码*" Width="*" 
+                <DataGridTextColumn Header="军网材料企业代码" Width="*" 
                                     Binding="{Binding SystemCode, UpdateSourceTrigger=PropertyChanged}"
                                     EditingElementStyle="{StaticResource TextColumnEditingElementStyle}"
                                     ElementStyle="{StaticResource TextColumnElementStyle}" />

+ 356 - 0
UniformMaterialManagementSystem/Views/SelectApplyContractDialog.xaml

@@ -0,0 +1,356 @@
+<UserControl x:Class="UniformMaterialManagementSystem.Views.SelectApplyContractDialog"
+             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
+             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
+             xmlns:local="clr-namespace:UniformMaterialManagementSystem.Views"
+             xmlns:utils="clr-namespace:UniformMaterialManagementSystem.Utils"
+             xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+             xmlns:behavoirs="clr-namespace:UniformMaterialManagementSystem.Behaviors"
+             xmlns:converters="clr-namespace:UniformMaterialManagementSystem.Converters"
+             xmlns:fdg="http://FilterDataGrid.Control.com/2024"
+             mc:Ignorable="d" 
+             d:DesignHeight="450" d:DesignWidth="800">
+
+    <UserControl.Resources>
+        <!-- 工具栏按钮模板 -->
+        <ControlTemplate x:Key="CustomToolBarButtomTemplate" TargetType="Button">
+            <!-- 按钮文本超出2个字,设置 Width 宽度自适应。再设置 Padding ,增加按钮左右的空白 -->
+            <Border Width="auto"
+             Background="{TemplateBinding Background}" 
+             CornerRadius="5">
+                <StackPanel HorizontalAlignment="Center" Orientation="Vertical">
+                    <TextBlock HorizontalAlignment="Center"
+                        FontFamily="{StaticResource FluentSystemIconsRegular}"
+                        FontSize="20"
+                        Text="{TemplateBinding Tag}" />
+                    <TextBlock Text="{TemplateBinding Content}"
+                        Padding="5 0 5 0" />
+                </StackPanel>
+            </Border>
+            <ControlTemplate.Triggers>
+                <Trigger Property="IsMouseOver" Value="True">
+                    <Setter Property="Background" Value="LightGray" />
+                </Trigger>
+            </ControlTemplate.Triggers>
+        </ControlTemplate>
+
+        <!-- 列标题模板:必须在列标题样式引用之前定义 -->
+        <ControlTemplate x:Key="CustomColumnHeaderTemplate" TargetType="DataGridColumnHeader">
+            <!-- BorderThickness 分别设置左,上,右,下的边框宽度。 -->
+            <Border BorderThickness="0, 0, 1, 1" BorderBrush="Black">
+                <Grid>
+                    <Grid.ColumnDefinitions>
+                        <ColumnDefinition />
+                        <ColumnDefinition Width="Auto" />
+                    </Grid.ColumnDefinitions>
+                    <!-- 重定义控件模板,在 Style 中设置的居中属性会被覆盖,所以在这里也需要设置居中 -->
+                    <ContentPresenter Grid.Column="0"
+                       HorizontalAlignment="Center" VerticalAlignment="Center" />
+                    <!-- 列排序图标 -->
+                    <Path x:Name="SortArrow"
+           Grid.Column="1"
+           Margin="0,0,5,0"
+           VerticalAlignment="Center"
+           Data="M 0 0 L 10 0 L 5 5 Z"
+           Fill="Gray"
+           Visibility="Collapsed" />
+                </Grid>
+            </Border>
+            <!-- 列排序按钮 -->
+            <ControlTemplate.Triggers>
+                <Trigger Property="SortDirection" Value="Ascending">
+                    <Setter TargetName="SortArrow" Property="Data" Value="M 0 5 L 10 5 L 5 0 Z" />
+                    <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
+                </Trigger>
+                <Trigger Property="SortDirection" Value="Descending">
+                    <Setter TargetName="SortArrow" Property="Data" Value="M 0 0 L 10 0 L 5 5 Z" />
+                    <Setter TargetName="SortArrow" Property="Visibility" Value="Visible" />
+                </Trigger>
+            </ControlTemplate.Triggers>
+        </ControlTemplate>
+
+        <!-- 列标题样式 -->
+        <Style x:Key="CustomColumnHeaderStyle" TargetType="DataGridColumnHeader">
+            <Setter Property="Background" Value="White" />
+            <Setter Property="Foreground" Value="Black" />
+            <Setter Property="FontWeight" Value="Bold" />
+            <Setter Property="HorizontalContentAlignment" Value="Center" />
+            <Setter Property="VerticalContentAlignment" Value="Center" />
+            <Setter Property="Height" Value="40" />
+            <Setter Property="Template" Value="{StaticResource CustomColumnHeaderTemplate}" />
+        </Style>
+
+        <!-- 行标题样式 -->
+        <Style x:Key="CustomRowHeaderStyle" TargetType="DataGridRowHeader">
+            <!-- 模板里面放 Label 控件,文本内容绑定 DataGridRow 的标题(行号) -->
+            <Setter Property="Template">
+                <Setter.Value>
+                    <ControlTemplate TargetType="DataGridRowHeader">
+                        <!-- 宽度跟行高相同 -->
+                        <Label Width="30"
+                        Background="White"
+                        BorderBrush="Black"
+                        BorderThickness="0 0 1 1"
+                        Content="{Binding Path=Header, RelativeSource={RelativeSource AncestorType=DataGridRow}}" 
+                        HorizontalContentAlignment="Center" />
+                    </ControlTemplate>
+                </Setter.Value>
+            </Setter>
+        </Style>
+
+        <!-- DataGrid 左上角样式 -->
+        <Style TargetType="Button" 
+        x:Key="{ComponentResourceKey ResourceId=DataGridSelectAllButtonStyle, TypeInTargetAssembly={x:Type DataGrid}}">
+            <!-- 模板里面放 Border 添加边框 -->
+            <Setter Property="Template">
+                <Setter.Value>
+                    <ControlTemplate TargetType="Button">
+                        <Border Background="White" 
+                         BorderBrush="Black"
+                         BorderThickness="0 0 1 1" />
+                    </ControlTemplate>
+                </Setter.Value>
+            </Setter>
+        </Style>
+
+        <!-- 行样式 -->
+        <Style x:Key="CustomRowStyle" TargetType="DataGridRow">
+            <Setter Property="Background" Value="White" />
+            <Setter Property="FontSize" Value="14" />
+            <Setter Property="Height" Value="30" />
+
+            <Style.Triggers>
+                <Trigger Property="AlternationIndex" Value="1">
+                    <Setter Property="Background" Value="WhiteSmoke" />
+                </Trigger>
+                <Trigger Property="IsSelected" Value="True">
+                    <Setter Property="Background" Value="LightBlue" />
+                </Trigger>
+            </Style.Triggers>
+        </Style>
+
+        <!-- 单元格样式 -->
+        <Style x:Key="CustomCellStyle" TargetType="DataGridCell"
+        BasedOn="{StaticResource {x:Type DataGridCell}}">
+            <Setter Property="Background" Value="White" />
+            <Setter Property="HorizontalContentAlignment" Value="Center" />
+            <Setter Property="VerticalContentAlignment" Value="Center" />
+            <Setter Property="Padding" Value="10" />
+            <Setter Property="BorderThickness" Value="0" />
+            <Style.Triggers>
+                <Trigger Property="IsSelected" Value="True">
+                    <Setter Property="Background" Value="LightBlue" />
+                    <Setter Property="Foreground" Value="Black" />
+                </Trigger>
+            </Style.Triggers>
+        </Style>
+
+        <!--  文本列居中  -->
+        <Style x:Key="TextColumnElementStyle" TargetType="TextBlock">
+            <Setter Property="HorizontalAlignment" Value="Center" />
+            <Setter Property="VerticalAlignment" Value="Center" />
+            <Setter Property="TextAlignment" Value="Center" />
+        </Style>
+
+        <!-- 复选框列居中 -->
+        <Style x:Key="CheckBoxColumnElementStyle" TargetType="CheckBox">
+            <Setter Property="HorizontalAlignment" Value="Center" />
+            <Setter Property="VerticalAlignment" Value="Center" />
+        </Style>
+
+        <!-- 转换器 -->
+        <converters:MultiParamConverter x:Key="MultiParamConverter" />
+    </UserControl.Resources>
+
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="auto" />
+            <RowDefinition Height="*" />
+            <RowDefinition Height="auto" />
+            <RowDefinition Height="*" />
+        </Grid.RowDefinitions>
+
+        <!-- 工具栏 -->
+        <Border Grid.Row="0"
+                BorderBrush="Gray"
+                BorderThickness="1">
+            <ToolBar Background="White"
+                     ToolBarTray.IsLocked="True">
+                <Button Content="确认选择"
+                        Tag="{x:Static utils:RegularFontUtil.Checkmark_32}"
+                        Template="{StaticResource CustomToolBarButtomTemplate}"
+                        Command="{Binding ConfirmCommand}">
+                    <Button.CommandParameter>
+                        <MultiBinding Converter="{StaticResource MultiParamConverter}">
+                            <Binding ElementName="fdgApply" />
+                            <Binding ElementName="fdgContract" />
+                            <Binding RelativeSource="{RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}" />
+                        </MultiBinding>
+                    </Button.CommandParameter>
+                </Button>
+                <Button Content="取消"
+                        Tag="{x:Static utils:RegularFontUtil.Dismiss_32}"
+                        Template="{StaticResource CustomToolBarButtomTemplate}"
+                        Command="{Binding CancelCommand}"
+                        CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"/>
+
+            </ToolBar>
+        </Border>
+
+        <!-- todo 不显示行号 -->
+        <!-- 检验申请列表 -->
+        <fdg:FilterDataGrid x:Name="fdgApply"
+                            Grid.Row="1"
+                            FilterLanguage="SimplifiedChinese"
+                            Background="White"
+                            AutoGenerateColumns="False"
+                            CanUserAddRows="False"
+                            behavoirs:DataGridBehavior.RowNumbers="True"
+                            SelectionMode="Single"
+                            SelectionUnit="FullRow"
+                            HeadersVisibility="All"
+                            ColumnHeaderStyle="{StaticResource CustomColumnHeaderStyle}"
+                            RowHeaderStyle="{StaticResource CustomRowHeaderStyle}"
+                            RowStyle="{StaticResource CustomRowStyle}"
+                            CellStyle="{StaticResource CustomCellStyle}"
+                            IsReadOnly="True"
+                            ItemsSource="{Binding InspectApplies, Mode=TwoWay}">
+
+            <!-- DataGrid 事件 -->
+            <b:Interaction.Triggers>
+                <!-- 选择行切换事件 -->
+                <b:EventTrigger EventName="SelectionChanged">
+                    <b:InvokeCommandAction Command="{Binding ApplySelectionChangedCommand}"
+                                           CommandParameter="{Binding ElementName=fdgApply}" />
+                </b:EventTrigger>
+            </b:Interaction.Triggers>
+
+            <!-- 数据结构 -->
+            <fdg:FilterDataGrid.Columns>
+                <fdg:FilterDataGridTextColumn Header="检验申请编号" Width="300"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ApplyNo}" />
+                <fdg:FilterDataGridTextColumn Header="检验类别" Width="100"
+                                              IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding InspCategory}" />
+                <fdg:FilterDataGridTextColumn Header="产品名称" Width="300"
+                                               IsColumnFiltered="True"
+                                              ElementStyle="{StaticResource TextColumnElementStyle}"
+                                              Binding="{Binding ProductName}" />
+                <fdg:FilterDataGridTextColumn Header="生产企业" Width="300"
+                                               IsColumnFiltered="True"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    Binding="{Binding Company}" />
+                <fdg:FilterDataGridTextColumn Header="报检数量" Width="100"
+                                               IsColumnFiltered="True"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    Binding="{Binding InspQuantity}" />
+                <fdg:FilterDataGridTextColumn Header="生产开始日期" Width="100"
+                                               IsColumnFiltered="True"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    Binding="{Binding StartProductDate, StringFormat='yyyy/MM/dd'}" />
+                <fdg:FilterDataGridTextColumn Header="生产结束日期" Width="100"
+                                               IsColumnFiltered="True"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    Binding="{Binding EndProductDate, StringFormat='yyyy/MM/dd'}" />
+                <fdg:FilterDataGridTextColumn Header="申请检验时间" Width="100"
+                                               IsColumnFiltered="True"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    Binding="{Binding InspDate, StringFormat='yyyy/MM/dd'}" />
+                <fdg:FilterDataGridCheckBoxColumn Header="检验报告生成状态" Width="100"
+                                               IsColumnFiltered="True"
+                                            ElementStyle="{StaticResource CheckBoxColumnElementStyle}"
+                                            Binding="{Binding ReportStatus}" />
+                <fdg:FilterDataGridTextColumn Header="申请收到时间" Width="100"
+                                               IsColumnFiltered="True"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    Binding="{Binding ApplyReceiveDate, StringFormat='yyyy/MM/dd'}" />
+                <fdg:FilterDataGridTextColumn Header="申请人" Width="100"
+                                               IsColumnFiltered="True"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    Binding="{Binding ApplyUser}" />
+                <fdg:FilterDataGridTextColumn Header="工作年度" Width="100"
+                                               IsColumnFiltered="True"
+                                    ElementStyle="{StaticResource TextColumnElementStyle}"
+                                    Binding="{Binding Year}" />
+                <fdg:FilterDataGridTextColumn Header="申请检验准备情况说明" Width="300"
+                                               IsColumnFiltered="True"
+                                    Binding="{Binding ApplyDescription}">
+                    <!-- 文本默认不换行,但多行文本依然会自动加宽行高,设置行样式之后行高固定 -->
+                    <!--<DataGridTextColumn.ElementStyle>
+                        <Style>
+                            <Setter Property="TextBlock.TextWrapping" Value="NoWrap" />
+                        </Style>
+                    </DataGridTextColumn.ElementStyle>-->
+                </fdg:FilterDataGridTextColumn>
+            </fdg:FilterDataGrid.Columns>
+        </fdg:FilterDataGrid>
+
+        <!-- 水平拖动分割线 -->
+        <GridSplitter Grid.Row="2" Height="5"  HorizontalAlignment="Stretch" />
+
+        <!-- 合同明细 -->
+        <fdg:FilterDataGrid x:Name="fdgContract"
+                            Grid.Row="3"
+                            FilterLanguage="SimplifiedChinese"
+                            Background="White"
+                            AutoGenerateColumns="False"
+                            CanUserAddRows="False"
+                            behavoirs:DataGridBehavior.RowNumbers="True"
+                            SelectionMode="Single"
+                            SelectionUnit="FullRow"
+                            HeadersVisibility="All"
+                            ColumnHeaderStyle="{StaticResource CustomColumnHeaderStyle}"
+                            RowHeaderStyle="{StaticResource CustomRowHeaderStyle}"
+                            RowStyle="{StaticResource CustomRowStyle}"
+                            CellStyle="{StaticResource CustomCellStyle}"
+                            IsReadOnly="True"
+                            ItemsSource="{Binding InspectApplyContracts, Mode=TwoWay}">
+
+            <!-- DataGrid 事件 -->
+            <b:Interaction.Triggers>
+                <!-- 加载事件:设置列宽 -->
+                <b:EventTrigger EventName="Loaded">
+                    <b:InvokeCommandAction Command="{Binding LoadedCommand}"
+                                           CommandParameter="{Binding ElementName=fdgContract}" />
+                </b:EventTrigger>
+
+                <!-- 选择行切换事件 -->
+                <!--<b:EventTrigger EventName="SelectionChanged">
+                    <b:InvokeCommandAction Command="{Binding ContractSelectionChangedCommand}"
+                                           CommandParameter="{Binding ElementName=fdgContract}" />
+                </b:EventTrigger>-->
+            </b:Interaction.Triggers>
+
+            <!-- todo 手动设置列宽? -->
+            <!-- 数据结构 -->
+            <fdg:FilterDataGrid.Columns>
+                <fdg:FilterDataGridTextColumn Header="合同编号" Width="200"
+                                                  IsColumnFiltered="True"
+                                                  ElementStyle="{StaticResource TextColumnElementStyle}"
+                                                  Binding="{Binding ContractNo}" />
+                <fdg:FilterDataGridTextColumn Header="采购机构" Width="200"
+                                                  IsColumnFiltered="True"
+                                                  ElementStyle="{StaticResource TextColumnElementStyle}"
+                                                  Binding="{Binding PurchaseCompany}" />
+                <!-- todo 是 string 类型的列? -->
+                <fdg:FilterDataGridTextColumn Header="合同签订时间" Width="200"
+                                                  IsColumnFiltered="True"
+                                                  ElementStyle="{StaticResource TextColumnElementStyle}"
+                                                  Binding="{Binding SigningDate, StringFormat='yyyy/MM/dd'}" />
+                <fdg:FilterDataGridTextColumn Header="合同数量" Width="200"
+                                                  IsColumnFiltered="True"
+                                                  ElementStyle="{StaticResource TextColumnElementStyle}"
+                                                  Binding="{Binding InspectQty}" />
+                <fdg:FilterDataGridTextColumn Header="可报检数量" Width="200"
+                                                  IsColumnFiltered="True"
+                                                  ElementStyle="{StaticResource TextColumnElementStyle}"
+                                                  Binding="{Binding UnInspectQty}" />
+            </fdg:FilterDataGrid.Columns>
+        </fdg:FilterDataGrid>
+    </Grid>
+
+</UserControl>

+ 33 - 0
UniformMaterialManagementSystem/Views/SelectApplyContractDialog.xaml.cs

@@ -0,0 +1,33 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using Microsoft.Extensions.DependencyInjection;
+using UniformMaterialManagementSystem.ViewModels;
+
+namespace UniformMaterialManagementSystem.Views
+{
+    /// <summary>
+    /// SelectApplyContractDialog.xaml 的交互逻辑
+    /// </summary>
+    public partial class SelectApplyContractDialog : UserControl
+    {
+        public SelectApplyContractDialog()
+        {
+            InitializeComponent();
+
+            // 绑定 ViewModel 
+            this.DataContext = App.Current.Services.GetService<SelectApplyContractDialogViewModel>();
+        }
+    }
+}