MES/ERP报表大致化元素组排类查询

根据MES/ERP报表类查询,大致化排序元素功能组排,在报表项目中自行先代码式预备相当一部分数据,或可以界面人为编辑添加数据,实现相关查询逻辑设计及相关Winform本地数据库代码项目

MES/ERP 报表查询 Winform 项目(带数据预置 + 手动编辑)

基于 SQLite 本地数据库,实现支持代码预置测试数据界面手动编辑数据多维度排序 / 分组查询的 MES/ERP 报表系统,覆盖生产工单、物料库存两类核心报表场景,兼顾数据管理与查询效率。

一、项目核心设计

1. 核心功能

  • 数据预置:启动时自动插入 50 + 条模拟数据(工单 / 库存),满足基础查询测试;
  • 手动编辑:界面支持新增 / 修改 / 删除单条数据,实时同步到本地数据库;
  • 高效查询:支持按编码、日期、车间 / 仓库排序,按多维度分组统计;
  • 数据校验:编辑数据时做合法性校验,保证数据一致性。

2. 项目结构

plaintext

复制代码
MES.ERP.Report.Editor/
├─ Data/                  // 数据层
│  ├─ SqliteHelper.cs     // SQLite数据库助手(事务+索引)
│  └─ DataService.cs      // 数据服务(预置/增删改查)
├─ Models/                // 实体模型
│  ├─ WorkOrderModel.cs   // 工单模型
│  └─ MaterialStockModel.cs // 库存模型
├─ UI/                    // 界面层
│  ├─ MainForm.cs         // 主查询窗体
│  ├─ DataEditForm.cs     // 数据编辑窗体
│  └─ FormDesigner/       // 设计器代码
├─ Utils/                 // 工具层
│  └─ QueryBuilder.cs     // 查询SQL构建器
└─ Program.cs             // 程序入口

二、完整代码实现

1. 数据库助手类(SqliteHelper.cs)

csharp

运行

复制代码
using System;
using System.Data;
using System.Data.SQLite;
using System.IO;

namespace MES.ERP.Report.Editor.Data
{
    public class SqliteHelper
    {
        // 数据库路径(程序目录)
        private static readonly string DbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mes_erp_report.db");
        private static readonly string ConnStr = $"Data Source={DbPath};Version=3;Pooling=true;Max Pool Size=50;Journal Mode=WAL;";

        /// <summary>
        /// 初始化数据库(创建表+索引)
        /// </summary>
        public static void InitDb()
        {
            if (!File.Exists(DbPath))
            {
                SQLiteConnection.CreateFile(DbPath);
                using (var conn = new SQLiteConnection(ConnStr))
                {
                    conn.Open();
                    // 1. 生产工单表+索引
                    ExecuteNonQuery(conn, @"
                        CREATE TABLE IF NOT EXISTS WorkOrder (
                            Id INTEGER PRIMARY KEY AUTOINCREMENT,
                            OrderCode TEXT NOT NULL UNIQUE,
                            ProductCode TEXT,
                            Workshop TEXT,
                            PlanQty DECIMAL,
                            ActualQty DECIMAL,
                            StartDate DATE,
                            Status TEXT,
                            CreateTime DATETIME DEFAULT CURRENT_TIMESTAMP
                        );
                        CREATE INDEX IF NOT EXISTS idx_wo_code ON WorkOrder(OrderCode);
                        CREATE INDEX IF NOT EXISTS idx_wo_date ON WorkOrder(StartDate);
                        CREATE INDEX IF NOT EXISTS idx_wo_workshop ON WorkOrder(Workshop);");

                    // 2. 物料库存表+索引
                    ExecuteNonQuery(conn, @"
                        CREATE TABLE IF NOT EXISTS MaterialStock (
                            Id INTEGER PRIMARY KEY AUTOINCREMENT,
                            MaterialCode TEXT NOT NULL UNIQUE,
                            MaterialName TEXT,
                            Warehouse TEXT,
                            StockQty DECIMAL,
                            SafeStockQty DECIMAL,
                            UpdateTime DATETIME DEFAULT CURRENT_TIMESTAMP
                        );
                        CREATE INDEX IF NOT EXISTS idx_ms_code ON MaterialStock(MaterialCode);
                        CREATE INDEX IF NOT EXISTS idx_ms_warehouse ON MaterialStock(Warehouse);");
                }
            }
        }

        /// <summary>
        /// 执行增删改(无返回值)
        /// </summary>
        private static void ExecuteNonQuery(SQLiteConnection conn, string sql, params SQLiteParameter[] parameters)
        {
            using (var cmd = new SQLiteCommand(sql, conn))
            {
                cmd.Parameters.AddRange(parameters);
                cmd.ExecuteNonQuery();
            }
        }

        /// <summary>
        /// 执行查询
        /// </summary>
        public static DataTable ExecuteQuery(string sql, params SQLiteParameter[] parameters)
        {
            using (var conn = new SQLiteConnection(ConnStr))
            {
                try
                {
                    conn.Open();
                    using (var cmd = new SQLiteCommand(sql, conn))
                    {
                        cmd.Parameters.AddRange(parameters);
                        using (var adapter = new SQLiteDataAdapter(cmd))
                        {
                            var dt = new DataTable();
                            adapter.Fill(dt);
                            return dt;
                        }
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("查询失败:" + ex.Message);
                }
            }
        }

        /// <summary>
        /// 执行增删改(带事务)
        /// </summary>
        public static int ExecuteNonQueryWithTrans(string sql, params SQLiteParameter[] parameters)
        {
            using (var conn = new SQLiteConnection(ConnStr))
            {
                conn.Open();
                using (var tran = conn.BeginTransaction())
                {
                    try
                    {
                        using (var cmd = new SQLiteCommand(sql, conn, tran))
                        {
                            cmd.Parameters.AddRange(parameters);
                            var result = cmd.ExecuteNonQuery();
                            tran.Commit();
                            return result;
                        }
                    }
                    catch (Exception ex)
                    {
                        tran.Rollback();
                        throw new Exception("数据操作失败:" + ex.Message);
                    }
                }
            }
        }
    }
}

2. 数据模型(Models)

WorkOrderModel.cs

csharp

运行

复制代码
using System;

namespace MES.ERP.Report.Editor.Models
{
    /// <summary>
    /// 生产工单模型
    /// </summary>
    public class WorkOrderModel
    {
        public int Id { get; set; }
        public string OrderCode { get; set; } // 工单编码(唯一)
        public string ProductCode { get; set; } // 产品编码
        public string Workshop { get; set; } // 车间
        public decimal PlanQty { get; set; } // 计划数量
        public decimal ActualQty { get; set; } // 实际数量
        public DateTime StartDate { get; set; } // 开工日期
        public string Status { get; set; } // 状态(待执行/执行中/已完成)
    }
}
MaterialStockModel.cs

csharp

运行

复制代码
namespace MES.ERP.Report.Editor.Models
{
    /// <summary>
    /// 物料库存模型
    /// </summary>
    public class MaterialStockModel
    {
        public int Id { get; set; }
        public string MaterialCode { get; set; } // 物料编码(唯一)
        public string MaterialName { get; set; } // 物料名称
        public string Warehouse { get; set; } // 仓库
        public decimal StockQty { get; set; } // 库存数量
        public decimal SafeStockQty { get; set; } // 安全库存
    }
}

3. 数据服务(DataService.cs)

封装数据预置、增删改查逻辑:

csharp

运行

复制代码
using System;
using System.Data;
using System.Data.SQLite;
using System.Linq;
using MES.ERP.Report.Editor.Models;

namespace MES.ERP.Report.Editor.Data
{
    public class DataService
    {
        /// <summary>
        /// 预置测试数据(工单+库存)
        /// </summary>
        public void PreloadTestData()
        {
            // 检查是否已有数据,避免重复插入
            var woCount = SqliteHelper.ExecuteQuery("SELECT COUNT(*) FROM WorkOrder").Rows[0][0];
            if (Convert.ToInt32(woCount) > 0) return;

            // 1. 预置50条工单数据
            for (int i = 1; i <= 50; i++)
            {
                var orderCode = $"WO{DateTime.Now:yyyyMMdd}{i:D3}";
                var productCode = $"PROD{(i % 10) + 1:D3}";
                var workshop = i % 3 == 0 ? "一车间" : i % 3 == 1 ? "二车间" : "三车间";
                var planQty = 1000 + i * 10;
                var actualQty = planQty - new Random().Next(0, 50);
                var startDate = DateTime.Now.AddDays(-new Random().Next(1, 30));
                var status = i % 3 == 0 ? "已完成" : i % 3 == 1 ? "执行中" : "待执行";

                var sql = @"
                    INSERT INTO WorkOrder (OrderCode, ProductCode, Workshop, PlanQty, ActualQty, StartDate, Status)
                    VALUES (@OrderCode, @ProductCode, @Workshop, @PlanQty, @ActualQty, @StartDate, @Status)";

                var parameters = new[]
                {
                    new SQLiteParameter("@OrderCode", orderCode),
                    new SQLiteParameter("@ProductCode", productCode),
                    new SQLiteParameter("@Workshop", workshop),
                    new SQLiteParameter("@PlanQty", planQty),
                    new SQLiteParameter("@ActualQty", actualQty),
                    new SQLiteParameter("@StartDate", startDate.ToString("yyyy-MM-dd")),
                    new SQLiteParameter("@Status", status)
                };

                SqliteHelper.ExecuteNonQueryWithTrans(sql, parameters);
            }

            // 2. 预置20条库存数据
            for (int i = 1; i <= 20; i++)
            {
                var materialCode = $"MAT{i:D3}";
                var materialName = i % 5 == 0 ? "钢材" : i % 5 == 1 ? "铝材" : i % 5 == 2 ? "塑料" : i % 5 == 3 ? "配件" : "辅料";
                var warehouse = i % 4 == 0 ? "原料仓" : i % 4 == 1 ? "半成品仓" : i % 4 == 2 ? "成品仓" : "备用仓";
                var stockQty = 5000 + i * 100;
                var safeStockQty = 2000 + i * 50;

                var sql = @"
                    INSERT INTO MaterialStock (MaterialCode, MaterialName, Warehouse, StockQty, SafeStockQty)
                    VALUES (@MaterialCode, @MaterialName, @Warehouse, @StockQty, @SafeStockQty)";

                var parameters = new[]
                {
                    new SQLiteParameter("@MaterialCode", materialCode),
                    new SQLiteParameter("@MaterialName", materialName),
                    new SQLiteParameter("@Warehouse", warehouse),
                    new SQLiteParameter("@StockQty", stockQty),
                    new SQLiteParameter("@SafeStockQty", safeStockQty)
                };

                SqliteHelper.ExecuteNonQueryWithTrans(sql, parameters);
            }
        }

        #region 工单数据操作
        /// <summary>
        /// 获取所有工单
        /// </summary>
        public DataTable GetAllWorkOrders()
        {
            return SqliteHelper.ExecuteQuery("SELECT * FROM WorkOrder ORDER BY StartDate DESC");
        }

        /// <summary>
        /// 添加工单
        /// </summary>
        public int AddWorkOrder(WorkOrderModel model)
        {
            var sql = @"
                INSERT INTO WorkOrder (OrderCode, ProductCode, Workshop, PlanQty, ActualQty, StartDate, Status)
                VALUES (@OrderCode, @ProductCode, @Workshop, @PlanQty, @ActualQty, @StartDate, @Status)";

            var parameters = new[]
            {
                new SQLiteParameter("@OrderCode", model.OrderCode),
                new SQLiteParameter("@ProductCode", model.ProductCode),
                new SQLiteParameter("@Workshop", model.Workshop),
                new SQLiteParameter("@PlanQty", model.PlanQty),
                new SQLiteParameter("@ActualQty", model.ActualQty),
                new SQLiteParameter("@StartDate", model.StartDate.ToString("yyyy-MM-dd")),
                new SQLiteParameter("@Status", model.Status)
            };

            return SqliteHelper.ExecuteNonQueryWithTrans(sql, parameters);
        }

        /// <summary>
        /// 修改工单
        /// </summary>
        public int UpdateWorkOrder(WorkOrderModel model)
        {
            var sql = @"
                UPDATE WorkOrder 
                SET ProductCode=@ProductCode, Workshop=@Workshop, PlanQty=@PlanQty, ActualQty=@ActualQty, StartDate=@StartDate, Status=@Status
                WHERE Id=@Id";

            var parameters = new[]
            {
                new SQLiteParameter("@ProductCode", model.ProductCode),
                new SQLiteParameter("@Workshop", model.Workshop),
                new SQLiteParameter("@PlanQty", model.PlanQty),
                new SQLiteParameter("@ActualQty", model.ActualQty),
                new SQLiteParameter("@StartDate", model.StartDate.ToString("yyyy-MM-dd")),
                new SQLiteParameter("@Status", model.Status),
                new SQLiteParameter("@Id", model.Id)
            };

            return SqliteHelper.ExecuteNonQueryWithTrans(sql, parameters);
        }

        /// <summary>
        /// 删除工单
        /// </summary>
        public int DeleteWorkOrder(int id)
        {
            var sql = "DELETE FROM WorkOrder WHERE Id=@Id";
            return SqliteHelper.ExecuteNonQueryWithTrans(sql, new SQLiteParameter("@Id", id));
        }
        #endregion

        #region 库存数据操作
        /// <summary>
        /// 获取所有库存
        /// </summary>
        public DataTable GetAllMaterialStocks()
        {
            return SqliteHelper.ExecuteQuery("SELECT * FROM MaterialStock ORDER BY MaterialCode");
        }

        /// <summary>
        /// 添加库存
        /// </summary>
        public int AddMaterialStock(MaterialStockModel model)
        {
            var sql = @"
                INSERT INTO MaterialStock (MaterialCode, MaterialName, Warehouse, StockQty, SafeStockQty)
                VALUES (@MaterialCode, @MaterialName, @Warehouse, @StockQty, @SafeStockQty)";

            var parameters = new[]
            {
                new SQLiteParameter("@MaterialCode", model.MaterialCode),
                new SQLiteParameter("@MaterialName", model.MaterialName),
                new SQLiteParameter("@Warehouse", model.Warehouse),
                new SQLiteParameter("@StockQty", model.StockQty),
                new SQLiteParameter("@SafeStockQty", model.SafeStockQty)
            };

            return SqliteHelper.ExecuteNonQueryWithTrans(sql, parameters);
        }

        /// <summary>
        /// 修改库存
        /// </summary>
        public int UpdateMaterialStock(MaterialStockModel model)
        {
            var sql = @"
                UPDATE MaterialStock 
                SET MaterialName=@MaterialName, Warehouse=@Warehouse, StockQty=@StockQty, SafeStockQty=@SafeStockQty
                WHERE Id=@Id";

            var parameters = new[]
            {
                new SQLiteParameter("@MaterialName", model.MaterialName),
                new SQLiteParameter("@Warehouse", model.Warehouse),
                new SQLiteParameter("@StockQty", model.StockQty),
                new SQLiteParameter("@SafeStockQty", model.SafeStockQty),
                new SQLiteParameter("@Id", model.Id)
            };

            return SqliteHelper.ExecuteNonQueryWithTrans(sql, parameters);
        }

        /// <summary>
        /// 删除库存
        /// </summary>
        public int DeleteMaterialStock(int id)
        {
            var sql = "DELETE FROM MaterialStock WHERE Id=@Id";
            return SqliteHelper.ExecuteNonQueryWithTrans(sql, new SQLiteParameter("@Id", id));
        }
        #endregion

        /// <summary>
        /// 报表查询(支持排序/分组)
        /// </summary>
        public DataTable QueryReport(string tableName, string sortField, string sortType, string groupField, string filterSql = "")
        {
            var baseSql = $"SELECT * FROM {tableName}";
            var whereSql = string.IsNullOrEmpty(filterSql) ? "" : $" WHERE {filterSql}";
            var groupSql = string.IsNullOrEmpty(groupField) || groupField == "无分组" ? "" : $" GROUP BY {groupField}";
            var sortSql = string.IsNullOrEmpty(sortField) ? "" : $" ORDER BY {sortField} {sortType}";

            var finalSql = $"{baseSql}{whereSql}{groupSql}{sortSql}";
            return SqliteHelper.ExecuteQuery(finalSql);
        }
    }
}

4. 查询 SQL 构建器(QueryBuilder.cs)

csharp

运行

复制代码
using System.Collections.Generic;

namespace MES.ERP.Report.Editor.Utils
{
    /// <summary>
    /// 查询条件构建器
    /// </summary>
    public class QueryBuilder
    {
        /// <summary>
        /// 构建筛选SQL
        /// </summary>
        public static string BuildFilterSql(string reportType, Dictionary<string, string> filterParams)
        {
            if (filterParams == null || filterParams.Count == 0)
                return "";

            var filterClauses = new List<string>();
            foreach (var param in filterParams)
            {
                var field = param.Key;
                var value = param.Value;

                if (string.IsNullOrEmpty(value))
                    continue;

                // 字段映射:UI字段 → 数据库字段
                var dbField = reportType switch
                {
                    "工单" => field switch
                    {
                        "工单编码" => "OrderCode",
                        "产品编码" => "ProductCode",
                        "车间" => "Workshop",
                        "状态" => "Status",
                        _ => field
                    },
                    "库存" => field switch
                    {
                        "物料编码" => "MaterialCode",
                        "物料名称" => "MaterialName",
                        "仓库" => "Warehouse",
                        _ => field
                    },
                    _ => field
                };

                filterClauses.Add($"{dbField} LIKE '%{value}%'");
            }

            return filterClauses.Count > 0 ? string.Join(" AND ", filterClauses) : "";
        }

        /// <summary>
        /// 构建排序字段映射
        /// </summary>
        public static string GetSortField(string reportType, string uiSortField)
        {
            return reportType switch
            {
                "工单" => uiSortField switch
                {
                    "工单编码" => "OrderCode",
                    "开工日期" => "StartDate",
                    "车间" => "Workshop",
                    "实际数量" => "ActualQty",
                    _ => "StartDate"
                },
                "库存" => uiSortField switch
                {
                    "物料编码" => "MaterialCode",
                    "仓库" => "Warehouse",
                    "库存数量" => "StockQty",
                    _ => "MaterialCode"
                },
                _ => "Id"
            };
        }

        /// <summary>
        /// 构建分组字段映射
        /// </summary>
        public static string GetGroupField(string reportType, string uiGroupField)
        {
            if (uiGroupField == "无分组")
                return "";

            return reportType switch
            {
                "工单" => uiGroupField switch
                {
                    "车间" => "Workshop",
                    "状态" => "Status",
                    "开工日期" => "StartDate",
                    _ => ""
                },
                "库存" => uiGroupField switch
                {
                    "仓库" => "Warehouse",
                    _ => ""
                },
                _ => ""
            };
        }
    }
}

5. 数据编辑窗体(DataEditForm.cs)

csharp

运行

复制代码
using System;
using System.Windows.Forms;
using MES.ERP.Report.Editor.Data;
using MES.ERP.Report.Editor.Models;

namespace MES.ERP.Report.Editor.UI
{
    public partial class DataEditForm : Form
    {
        private readonly DataService _dataService = new DataService();
        private readonly string _dataType; // 工单/库存
        private int _editId = -1; // 编辑ID(-1表示新增)

        /// <summary>
        /// 构造函数(新增)
        /// </summary>
        public DataEditForm(string dataType)
        {
            InitializeComponent();
            _dataType = dataType;
            this.Text = $"新增{(_dataType == "工单" ? "生产工单" : "物料库存")}";
            InitForm();
        }

        /// <summary>
        /// 构造函数(编辑)
        /// </summary>
        public DataEditForm(string dataType, DataGridViewRow row)
        {
            InitializeComponent();
            _dataType = dataType;
            _editId = Convert.ToInt32(row.Cells["Id"].Value);
            this.Text = $"编辑{(_dataType == "工单" ? "生产工单" : "物料库存")}";
            InitForm();
            LoadRowData(row);
        }

        /// <summary>
        /// 初始化窗体
        /// </summary>
        private void InitForm()
        {
            if (_dataType == "工单")
            {
                // 显示工单相关控件
                lblCode.Text = "工单编码:";
                lblName.Text = "产品编码:";
                lblThird.Text = "车间:";
                lblFourth.Text = "计划数量:";
                lblFifth.Text = "实际数量:";
                lblSixth.Text = "开工日期:";
                lblSeventh.Text = "状态:";

                txtFourth.Text = "0";
                txtFifth.Text = "0";
                dtpSixth.Visible = true;
                cboSeventh.Visible = true;
                cboSeventh.Items.AddRange(new[] { "待执行", "执行中", "已完成" });
                cboSeventh.SelectedIndex = 0;
            }
            else
            {
                // 显示库存相关控件
                lblCode.Text = "物料编码:";
                lblName.Text = "物料名称:";
                lblThird.Text = "仓库:";
                lblFourth.Text = "库存数量:";
                lblFifth.Text = "安全库存:";
                lblSixth.Visible = false;
                dtpSixth.Visible = false;
                lblSeventh.Visible = false;
                cboSeventh.Visible = false;

                txtFourth.Text = "0";
                txtFifth.Text = "0";
            }
        }

        /// <summary>
        /// 加载行数据
        /// </summary>
        private void LoadRowData(DataGridViewRow row)
        {
            if (_dataType == "工单")
            {
                txtCode.Text = row.Cells["OrderCode"].Value.ToString();
                txtName.Text = row.Cells["ProductCode"].Value.ToString();
                txtThird.Text = row.Cells["Workshop"].Value.ToString();
                txtFourth.Text = row.Cells["PlanQty"].Value.ToString();
                txtFifth.Text = row.Cells["ActualQty"].Value.ToString();
                dtpSixth.Value = Convert.ToDateTime(row.Cells["StartDate"].Value);
                cboSeventh.Text = row.Cells["Status"].Value.ToString();
            }
            else
            {
                txtCode.Text = row.Cells["MaterialCode"].Value.ToString();
                txtName.Text = row.Cells["MaterialName"].Value.ToString();
                txtThird.Text = row.Cells["Warehouse"].Value.ToString();
                txtFourth.Text = row.Cells["StockQty"].Value.ToString();
                txtFifth.Text = row.Cells["SafeStockQty"].Value.ToString();
            }

            txtCode.ReadOnly = true; // 编码不可修改
        }

        /// <summary>
        /// 保存按钮
        /// </summary>
        private void btnSave_Click(object sender, EventArgs e)
        {
            try
            {
                // 数据校验
                if (string.IsNullOrEmpty(txtCode.Text.Trim()))
                {
                    MessageBox.Show($"{(_dataType == "工单" ? "工单" : "物料")}编码不能为空!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }

                if (!decimal.TryParse(txtFourth.Text.Trim(), out decimal qty1) || qty1 < 0)
                {
                    MessageBox.Show("数量必须为非负数!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }

                if (!decimal.TryParse(txtFifth.Text.Trim(), out decimal qty2) || qty2 < 0)
                {
                    MessageBox.Show("数量必须为非负数!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return;
                }

                // 执行保存
                int result = 0;
                if (_dataType == "工单")
                {
                    var model = new WorkOrderModel
                    {
                        Id = _editId,
                        OrderCode = txtCode.Text.Trim(),
                        ProductCode = txtName.Text.Trim(),
                        Workshop = txtThird.Text.Trim(),
                        PlanQty = qty1,
                        ActualQty = qty2,
                        StartDate = dtpSixth.Value,
                        Status = cboSeventh.Text.Trim()
                    };

                    result = _editId == -1 ? _dataService.AddWorkOrder(model) : _dataService.UpdateWorkOrder(model);
                }
                else
                {
                    var model = new MaterialStockModel
                    {
                        Id = _editId,
                        MaterialCode = txtCode.Text.Trim(),
                        MaterialName = txtName.Text.Trim(),
                        Warehouse = txtThird.Text.Trim(),
                        StockQty = qty1,
                        SafeStockQty = qty2
                    };

                    result = _editId == -1 ? _dataService.AddMaterialStock(model) : _dataService.UpdateMaterialStock(model);
                }

                if (result > 0)
                {
                    MessageBox.Show("保存成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    this.DialogResult = DialogResult.OK;
                    this.Close();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("保存失败:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// 取消按钮
        /// </summary>
        private void btnCancel_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        #region 窗体设计器代码
        private Label lblCode;
        private TextBox txtCode;
        private Label lblName;
        private TextBox txtName;
        private Label lblThird;
        private TextBox txtThird;
        private Label lblFourth;
        private TextBox txtFourth;
        private Label lblFifth;
        private TextBox txtFifth;
        private Label lblSixth;
        private DateTimePicker dtpSixth;
        private Label lblSeventh;
        private ComboBox cboSeventh;
        private Button btnSave;
        private Button btnCancel;

        private void InitializeComponent()
        {
            this.lblCode = new System.Windows.Forms.Label();
            this.txtCode = new System.Windows.Forms.TextBox();
            this.lblName = new System.Windows.Forms.Label();
            this.txtName = new System.Windows.Forms.TextBox();
            this.lblThird = new System.Windows.Forms.Label();
            this.txtThird = new System.Windows.Forms.TextBox();
            this.lblFourth = new System.Windows.Forms.Label();
            this.txtFourth = new System.Windows.Forms.TextBox();
            this.lblFifth = new System.Windows.Forms.Label();
            this.txtFifth = new System.Windows.Forms.TextBox();
            this.lblSixth = new System.Windows.Forms.Label();
            this.dtpSixth = new System.Windows.Forms.DateTimePicker();
            this.lblSeventh = new System.Windows.Forms.Label();
            this.cboSeventh = new System.Windows.Forms.ComboBox();
            this.btnSave = new System.Windows.Forms.Button();
            this.btnCancel = new System.Windows.Forms.Button();
            this.SuspendLayout();

            // lblCode
            this.lblCode.AutoSize = true;
            this.lblCode.Location = new System.Drawing.Point(30, 30);
            this.lblCode.Name = "lblCode";
            this.lblCode.Size = new System.Drawing.Size(62, 17);
            this.lblCode.Text = "编码:";

            // txtCode
            this.txtCode.Location = new System.Drawing.Point(98, 27);
            this.txtCode.Name = "txtCode";
            this.txtCode.Size = new System.Drawing.Size(200, 23);
            this.txtCode.TabIndex = 1;

            // lblName
            this.lblName.AutoSize = true;
            this.lblName.Location = new System.Drawing.Point(30, 60);
            this.lblName.Name = "lblName";
            this.lblName.Size = new System.Drawing.Size(62, 17);
            this.lblName.Text = "名称:";

            // txtName
            this.txtName.Location = new System.Drawing.Point(98, 57);
            this.txtName.Name = "txtName";
            this.txtName.Size = new System.Drawing.Size(200, 23);
            this.txtName.TabIndex = 3;

            // lblThird
            this.lblThird.AutoSize = true;
            this.lblThird.Location = new System.Drawing.Point(30, 90);
            this.lblThird.Name = "lblThird";
            this.lblThird.Size = new System.Drawing.Size(62, 17);
            this.lblThird.Text = "类型:";

            // txtThird
            this.txtThird.Location = new System.Drawing.Point(98, 87);
            this.txtThird.Name = "txtThird";
            this.txtThird.Size = new System.Drawing.Size(200, 23);
            this.txtThird.TabIndex = 5;

            // lblFourth
            this.lblFourth.AutoSize = true;
            this.lblFourth.Location = new System.Drawing.Point(30, 120);
            this.lblFourth.Name = "lblFourth";
            this.lblFourth.Size = new System.Drawing.Size(62, 17);
            this.lblFourth.Text = "数量1:";

            // txtFourth
            this.txtFourth.Location = new System.Drawing.Point(98, 117);
            this.txtFourth.Name = "txtFourth";
            this.txtFourth.Size = new System.Drawing.Size(200, 23);
            this.txtFourth.TabIndex = 7;

            // lblFifth
            this.lblFifth.AutoSize = true;
            this.lblFifth.Location = new System.Drawing.Point(30, 150);
            this.lblFifth.Name = "lblFifth";
            this.lblFifth.Size = new System.Drawing.Size(62, 17);
            this.lblFifth.Text = "数量2:";

            // txtFifth
            this.txtFifth.Location = new System.Drawing.Point(98, 147);
            this.txtFifth.Name = "txtFifth";
            this.txtFifth.Size = new System.Drawing.Size(200, 23);
            this.txtFifth.TabIndex = 9;

            // lblSixth
            this.lblSixth.AutoSize = true;
            this.lblSixth.Location = new System.Drawing.Point(30, 180);
            this.lblSixth.Name = "lblSixth";
            this.lblSixth.Size = new System.Drawing.Size(62, 17);
            this.lblSixth.Text = "日期:";

            // dtpSixth
            this.dtpSixth.Format = System.Windows.Forms.DateTimePickerFormat.Short;
            this.dtpSixth.Location = new System.Drawing.Point(98, 177);
            this.dtpSixth.Name = "dtpSixth";
            this.dtpSixth.Size = new System.Drawing.Size(200, 23);
            this.dtpSixth.TabIndex = 11;

            // lblSeventh
            this.lblSeventh.AutoSize = true;
            this.lblSeventh.Location = new System.Drawing.Point(30, 210);
            this.lblSeventh.Name = "lblSeventh";
            this.lblSeventh.Size = new System.Drawing.Size(62, 17);
            this.lblSeventh.Text = "状态:";

            // cboSeventh
            this.cboSeventh.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboSeventh.Location = new System.Drawing.Point(98, 207);
            this.cboSeventh.Name = "cboSeventh";
            this.cboSeventh.Size = new System.Drawing.Size(200, 23);
            this.cboSeventh.TabIndex = 13;

            // btnSave
            this.btnSave.Location = new System.Drawing.Point(98, 240);
            this.btnSave.Name = "btnSave";
            this.btnSave.Size = new System.Drawing.Size(90, 30);
            this.btnSave.Text = "保存";
            this.btnSave.Click += new System.EventHandler(this.btnSave_Click);

            // btnCancel
            this.btnCancel.Location = new System.Drawing.Point(208, 240);
            this.btnCancel.Name = "btnCancel";
            this.btnCancel.Size = new System.Drawing.Size(90, 30);
            this.btnCancel.Text = "取消";
            this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click);

            // DataEditForm
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 17F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(330, 290);
            this.Controls.Add(this.btnCancel);
            this.Controls.Add(this.btnSave);
            this.Controls.Add(this.cboSeventh);
            this.Controls.Add(this.lblSeventh);
            this.Controls.Add(this.dtpSixth);
            this.Controls.Add(this.lblSixth);
            this.Controls.Add(this.txtFifth);
            this.Controls.Add(this.lblFifth);
            this.Controls.Add(this.txtFourth);
            this.Controls.Add(this.lblFourth);
            this.Controls.Add(this.txtThird);
            this.Controls.Add(this.lblThird);
            this.Controls.Add(this.txtName);
            this.Controls.Add(this.lblName);
            this.Controls.Add(this.txtCode);
            this.Controls.Add(this.lblCode);
            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "DataEditForm";
            this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
            this.ResumeLayout(false);
            this.PerformLayout();
        }
        #endregion
    }
}

6. 主查询窗体(MainForm.cs)

csharp

运行

复制代码
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;
using MES.ERP.Report.Editor.Data;
using MES.ERP.Report.Editor.Utils;

namespace MES.ERP.Report.Editor.UI
{
    public partial class MainForm : Form
    {
        private readonly DataService _dataService = new DataService();

        public MainForm()
        {
            InitializeComponent();
            // 初始化数据库
            SqliteHelper.InitDb();
            // 预置测试数据
            _dataService.PreloadTestData();
            // 初始化界面
            InitUI();
            // 加载默认数据
            LoadData();
        }

        /// <summary>
        /// 初始化界面
        /// </summary>
        private void InitUI()
        {
            // 报表类型
            cboReportType.Items.AddRange(new[] { "工单", "库存" });
            cboReportType.SelectedIndex = 0;
            cboReportType.SelectedIndexChanged += (s, e) => LoadData();

            // 排序字段
            cboSortField.Items.AddRange(new[] { "工单编码", "开工日期", "车间", "实际数量" });
            cboSortField.SelectedIndex = 1;

            // 排序类型
            cboSortType.Items.AddRange(new[] { "升序", "降序" });
            cboSortType.SelectedIndex = 1;

            // 分组字段
            cboGroupField.Items.AddRange(new[] { "无分组", "车间", "状态", "开工日期" });
            cboGroupField.SelectedIndex = 0;

            // DataGridView样式
            dgvReport.AutoGenerateColumns = false;
            dgvReport.ReadOnly = true;
            dgvReport.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            dgvReport.AllowUserToAddRows = false;

            // 绑定工单列
            BindWorkOrderColumns();
        }

        /// <summary>
        /// 绑定工单列
        /// </summary>
        private void BindWorkOrderColumns()
        {
            dgvReport.Columns.Clear();
            if (cboReportType.SelectedItem.ToString() == "工单")
            {
                dgvReport.Columns.AddRange(new[]
                {
                    new DataGridViewTextBoxColumn { Name = "Id", HeaderText = "ID", DataPropertyName = "Id", Visible = false },
                    new DataGridViewTextBoxColumn { Name = "OrderCode", HeaderText = "工单编码", DataPropertyName = "OrderCode", Width = 120 },
                    new DataGridViewTextBoxColumn { Name = "ProductCode", HeaderText = "产品编码", DataPropertyName = "ProductCode", Width = 100 },
                    new DataGridViewTextBoxColumn { Name = "Workshop", HeaderText = "车间", DataPropertyName = "Workshop", Width = 80 },
                    new DataGridViewTextBoxColumn { Name = "PlanQty", HeaderText = "计划数量", DataPropertyName = "PlanQty", Width = 80 },
                    new DataGridViewTextBoxColumn { Name = "ActualQty", HeaderText = "实际数量", DataPropertyName = "ActualQty", Width = 80 },
                    new DataGridViewTextBoxColumn { Name = "StartDate", HeaderText = "开工日期", DataPropertyName = "StartDate", Width = 100 },
                    new DataGridViewTextBoxColumn { Name = "Status", HeaderText = "状态", DataPropertyName = "Status", Width = 80 }
                });
            }
            else
            {
                dgvReport.Columns.AddRange(new[]
                {
                    new DataGridViewTextBoxColumn { Name = "Id", HeaderText = "ID", DataPropertyName = "Id", Visible = false },
                    new DataGridViewTextBoxColumn { Name = "MaterialCode", HeaderText = "物料编码", DataPropertyName = "MaterialCode", Width = 120 },
                    new DataGridViewTextBoxColumn { Name = "MaterialName", HeaderText = "物料名称", DataPropertyName = "MaterialName", Width = 100 },
                    new DataGridViewTextBoxColumn { Name = "Warehouse", HeaderText = "仓库", DataPropertyName = "Warehouse", Width = 80 },
                    new DataGridViewTextBoxColumn { Name = "StockQty", HeaderText = "库存数量", DataPropertyName = "StockQty", Width = 80 },
                    new DataGridViewTextBoxColumn { Name = "SafeStockQty", HeaderText = "安全库存", DataPropertyName = "SafeStockQty", Width = 80 }
                });

                // 更新排序/分组字段
                cboSortField.Items.Clear();
                cboSortField.Items.AddRange(new[] { "物料编码", "仓库", "库存数量" });
                cboSortField.SelectedIndex = 0;

                cboGroupField.Items.Clear();
                cboGroupField.Items.AddRange(new[] { "无分组", "仓库" });
                cboGroupField.SelectedIndex = 0;
            }
        }

        /// <summary>
        /// 加载数据
        /// </summary>
        private void LoadData()
        {
            var reportType = cboReportType.SelectedItem.ToString();
            DataTable dt = reportType == "工单" ? _dataService.GetAllWorkOrders() : _dataService.GetAllMaterialStocks();
            dgvReport.DataSource = dt;

            // 更新统计信息
            UpdateStatistics(reportType, dt);
        }

        /// <summary>
        /// 更新统计信息
        /// </summary>
        private void UpdateStatistics(string reportType, DataTable dt)
        {
            lblTotal.Text = $"总记录数:{dt.Rows.Count}";
            if (dt.Rows.Count == 0)
            {
                lblSum.Text = "总计数量:0";
                return;
            }

            decimal sum = 0;
            if (reportType == "工单")
            {
                sum = dt.AsEnumerable().Sum(row => Convert.ToDecimal(row["ActualQty"]));
            }
            else
            {
                sum = dt.AsEnumerable().Sum(row => Convert.ToDecimal(row["StockQty"]));
            }

            lblSum.Text = $"总计数量:{sum:F2}";
        }

        /// <summary>
        /// 查询按钮
        /// </summary>
        private void btnQuery_Click(object sender, EventArgs e)
        {
            try
            {
                var reportType = cboReportType.SelectedItem.ToString();
                var tableName = reportType == "工单" ? "WorkOrder" : "MaterialStock";

                // 构建筛选条件
                var filterParams = new Dictionary<string, string>
                {
                    { reportType == "工单" ? "工单编码" : "物料编码", txtFilterCode.Text.Trim() },
                    { reportType == "工单" ? "车间" : "仓库", txtFilterType.Text.Trim() }
                };
                var filterSql = QueryBuilder.BuildFilterSql(reportType, filterParams);

                // 构建排序/分组
                var sortField = QueryBuilder.GetSortField(reportType, cboSortField.SelectedItem.ToString());
                var sortType = cboSortType.SelectedItem.ToString() == "升序" ? "ASC" : "DESC";
                var groupField = QueryBuilder.GetGroupField(reportType, cboGroupField.SelectedItem.ToString());

                // 执行查询
                var dt = _dataService.QueryReport(tableName, sortField, sortType, groupField, filterSql);
                dgvReport.DataSource = dt;

                // 更新统计
                UpdateStatistics(reportType, dt);
            }
            catch (Exception ex)
            {
                MessageBox.Show("查询失败:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// 重置按钮
        /// </summary>
        private void btnReset_Click(object sender, EventArgs e)
        {
            txtFilterCode.Clear();
            txtFilterType.Clear();
            cboSortField.SelectedIndex = reportType == "工单" ? 1 : 0;
            cboSortType.SelectedIndex = 1;
            cboGroupField.SelectedIndex = 0;
            LoadData();
        }

        /// <summary>
        /// 新增按钮
        /// </summary>
        private void btnAdd_Click(object sender, EventArgs e)
        {
            var reportType = cboReportType.SelectedItem.ToString();
            var editForm = new DataEditForm(reportType);
            if (editForm.ShowDialog() == DialogResult.OK)
            {
                LoadData();
            }
        }

        /// <summary>
        /// 编辑按钮
        /// </summary>
        private void btnEdit_Click(object sender, EventArgs e)
        {
            if (dgvReport.SelectedRows.Count == 0)
            {
                MessageBox.Show("请选择要编辑的行!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            var reportType = cboReportType.SelectedItem.ToString();
            var editForm = new DataEditForm(reportType, dgvReport.SelectedRows[0]);
            if (editForm.ShowDialog() == DialogResult.OK)
            {
                LoadData();
            }
        }

        /// <summary>
        /// 删除按钮
        /// </summary>
        private void btnDelete_Click(object sender, EventArgs e)
        {
            if (dgvReport.SelectedRows.Count == 0)
            {
                MessageBox.Show("请选择要删除的行!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (MessageBox.Show("确定要删除选中的数据吗?", "确认", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
                return;

            try
            {
                var id = Convert.ToInt32(dgvReport.SelectedRows[0].Cells["Id"].Value);
                var reportType = cboReportType.SelectedItem.ToString();

                int result = reportType == "工单" 
                    ? _dataService.DeleteWorkOrder(id) 
                    : _dataService.DeleteMaterialStock(id);

                if (result > 0)
                {
                    MessageBox.Show("删除成功!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    LoadData();
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show("删除失败:" + ex.Message, "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        #region 窗体设计器代码
        private ComboBox cboReportType;
        private Label label1;
        private TextBox txtFilterCode;
        private Label label2;
        private TextBox txtFilterType;
        private Button btnQuery;
        private Button btnReset;
        private ComboBox cboSortField;
        private Label label3;
        private ComboBox cboSortType;
        private Label label4;
        private ComboBox cboGroupField;
        private Label label5;
        private DataGridView dgvReport;
        private Label lblTotal;
        private Label lblSum;
        private Button btnAdd;
        private Button btnEdit;
        private Button btnDelete;

        private void InitializeComponent()
        {
            this.cboReportType = new System.Windows.Forms.ComboBox();
            this.label1 = new System.Windows.Forms.Label();
            this.txtFilterCode = new System.Windows.Forms.TextBox();
            this.label2 = new System.Windows.Forms.Label();
            this.txtFilterType = new System.Windows.Forms.TextBox();
            this.btnQuery = new System.Windows.Forms.Button();
            this.btnReset = new System.Windows.Forms.Button();
            this.cboSortField = new System.Windows.Forms.ComboBox();
            this.label3 = new System.Windows.Forms.Label();
            this.cboSortType = new System.Windows.Forms.ComboBox();
            this.label4 = new System.Windows.Forms.Label();
            this.cboGroupField = new System.Windows.Forms.ComboBox();
            this.label5 = new System.Windows.Forms.Label();
            this.dgvReport = new System.Windows.Forms.DataGridView();
            this.lblTotal = new System.Windows.Forms.Label();
            this.lblSum = new System.Windows.Forms.Label();
            this.btnAdd = new System.Windows.Forms.Button();
            this.btnEdit = new System.Windows.Forms.Button();
            this.btnDelete = new System.Windows.Forms.Button();
            ((System.ComponentModel.ISupportInitialize)(this.dgvReport)).BeginInit();
            this.SuspendLayout();

            // cboReportType
            this.cboReportType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboReportType.Location = new System.Drawing.Point(80, 15);
            this.cboReportType.Name = "cboReportType";
            this.cboReportType.Size = new System.Drawing.Size(80, 23);
            this.cboReportType.TabIndex = 0;

            // label1
            this.label1.AutoSize = true;
            this.label1.Location = new System.Drawing.Point(12, 18);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(62, 17);
            this.label1.Text = "报表类型:";

            // txtFilterCode
            this.txtFilterCode.Location = new System.Drawing.Point(238, 15);
            this.txtFilterCode.Name = "txtFilterCode";
            this.txtFilterCode.PlaceholderText = "编码筛选";
            this.txtFilterCode.Size = new System.Drawing.Size(100, 23);
            this.txtFilterCode.TabIndex = 2;

            // label2
            this.label2.AutoSize = true;
            this.label2.Location = new System.Drawing.Point(186, 18);
            this.label2.Name = "label2";
            this.label2.Size = new System.Drawing.Size(46, 17);
            this.label2.Text = "编码:";

            // txtFilterType
            this.txtFilterType.Location = new System.Drawing.Point(394, 15);
            this.txtFilterType.Name = "txtFilterType";
            this.txtFilterType.PlaceholderText = "车间/仓库";
            this.txtFilterType.Size = new System.Drawing.Size(100, 23);
            this.txtFilterType.TabIndex = 4;

            // btnQuery
            this.btnQuery.Location = new System.Drawing.Point(500, 15);
            this.btnQuery.Name = "btnQuery";
            this.btnQuery.Size = new System.Drawing.Size(75, 23);
            this.btnQuery.Text = "查询";
            this.btnQuery.Click += new System.EventHandler(this.btnQuery_Click);

            // btnReset
            this.btnReset.Location = new System.Drawing.Point(581, 15);
            this.btnReset.Name = "btnReset";
            this.btnReset.Size = new System.Drawing.Size(75, 23);
            this.btnReset.Text = "重置";
            this.btnReset.Click += new System.EventHandler(this.btnReset_Click);

            // cboSortField
            this.cboSortField.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboSortField.Location = new System.Drawing.Point(80, 50);
            this.cboSortField.Name = "cboSortField";
            this.cboSortField.Size = new System.Drawing.Size(100, 23);
            this.cboSortField.TabIndex = 6;

            // label3
            this.label3.AutoSize = true;
            this.label3.Location = new System.Drawing.Point(12, 53);
            this.label3.Name = "label3";
            this.label3.Size = new System.Drawing.Size(62, 17);
            this.label3.Text = "排序字段:";

            // cboSortType
            this.cboSortType.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboSortType.Location = new System.Drawing.Point(238, 50);
            this.cboSortType.Name = "cboSortType";
            this.cboSortType.Size = new System.Drawing.Size(80, 23);
            this.cboSortType.TabIndex = 8;

            // label4
            this.label4.AutoSize = true;
            this.label4.Location = new System.Drawing.Point(186, 53);
            this.label4.Name = "label4";
            this.label4.Size = new System.Drawing.Size(46, 17);
            this.label4.Text = "排序:";

            // cboGroupField
            this.cboGroupField.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
            this.cboGroupField.Location = new System.Drawing.Point(394, 50);
            this.cboGroupField.Name = "cboGroupField";
            this.cboGroupField.Size = new System.Drawing.Size(100, 23);
            this.cboGroupField.TabIndex = 10;

            // label5
            this.label5.AutoSize = true;
            this.label5.Location = new System.Drawing.Point(344, 53);
            this.label5.Name = "label5";
            this.label5.Size = new System.Drawing.Size(46, 17);
            this.label5.Text = "分组:";

            // dgvReport
            this.dgvReport.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 
            | System.Windows.Forms.AnchorStyles.Left) 
            | System.Windows.Forms.AnchorStyles.Right)));
            this.dgvReport.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize;
            this.dgvReport.Location = new System.Drawing.Point(12, 100);
            this.dgvReport.Name = "dgvReport";
            this.dgvReport.RowHeadersWidth = 51;
            this.dgvReport.RowTemplate.Height = 29;
            this.dgvReport.Size = new System.Drawing.Size(700, 400);
            this.dgvReport.TabIndex = 11;

            // lblTotal
            this.lblTotal.AutoSize = true;
            this.lblTotal.Location = new System.Drawing.Point(12, 80);
            this.lblTotal.Name = "lblTotal";
            this.lblTotal.Size = new System.Drawing.Size(80, 17);
            this.lblTotal.Text = "总记录数:0";

            // lblSum
            this.lblSum.AutoSize = true;
            this.lblSum.Location = new System.Drawing.Point(98, 80);
            this.lblSum.Name = "lblSum";
            this.lblSum.Size = new System.Drawing.Size(80, 17);
            this.lblSum.Text = "总计数量:0";

            // btnAdd
            this.btnAdd.Location = new System.Drawing.Point(600, 50);
            this.btnAdd.Name = "btnAdd";
            this.btnAdd.Size = new System.Drawing.Size(75, 23);
            this.btnAdd.Text = "新增";
            this.btnAdd.Click += new System.EventHandler(this.btnAdd_Click);

            // btnEdit
            this.btnEdit.Location = new System.Drawing.Point(681, 50);
            this.btnEdit.Name = "btnEdit";
            this.btnEdit.Size = new System.Drawing.Size(75, 23);
            this.btnEdit.Text = "编辑";
            this.btnEdit.Click += new System.EventHandler(this.btnEdit_Click);

            // btnDelete
            this.btnDelete.Location = new System.Drawing.Point(762, 50);
            this.btnDelete.Name = "btnDelete";
            this.btnDelete.Size = new System.Drawing.Size(75, 23);
            this.btnDelete.Text = "删除";
            this.btnDelete.Click += new System.EventHandler(this.btnDelete_Click);

            // MainForm
            this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 17F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(850, 512);
            this.Controls.Add(this.btnDelete);
            this.Controls.Add(this.btnEdit);
            this.Controls.Add(this.btnAdd);
            this.Controls.Add(this.lblSum);
            this.Controls.Add(this.lblTotal);
            this.Controls.Add(this.dgvReport);
            this.Controls.Add(this.cboGroupField);
            this.Controls.Add(this.label5);
            this.Controls.Add(this.cboSortType);
            this.Controls.Add(this.label4);
            this.Controls.Add(this.cboSortField);
            this.Controls.Add(this.label3);
            this.Controls.Add(this.btnReset);
            this.Controls.Add(this.btnQuery);
            this.Controls.Add(this.txtFilterType);
            this.Controls.Add(this.label2);
            this.Controls.Add(this.txtFilterCode);
            this.Controls.Add(this.label1);
            this.Controls.Add(this.cboReportType);
            this.Name = "MainForm";
            this.Text = "MES/ERP 报表查询与数据管理系统";
            ((System.ComponentModel.ISupportInitialize)(this.dgvReport)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();
        }
        #endregion
    }
}

7. 程序入口(Program.cs)

csharp

运行

复制代码
using System;
using System.Windows.Forms;
using MES.ERP.Report.Editor.UI;

namespace MES.ERP.Report.Editor
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
    }
}

三、核心功能说明

1. 数据预置

  • 程序启动时自动检查数据库,若无数据则插入 50 条工单 + 20 条库存模拟数据;
  • 数据覆盖多车间、多状态、多日期,满足多样化查询测试。

2. 手动编辑

  • 新增:点击「新增」按钮,弹出编辑窗体,输入数据后保存;
  • 编辑:选中行后点击「编辑」,修改非编码字段(编码唯一不可改);
  • 删除:选中行后点击「删除」,确认后删除数据;
  • 数据校验:数量字段做非负校验,编码字段做非空校验。

3. 高效查询

  • 筛选:支持按编码、车间 / 仓库模糊筛选;
  • 排序:工单支持按编码 / 日期 / 车间 / 数量排序,库存支持按编码 / 仓库 / 数量排序;
  • 分组:工单支持按车间 / 状态 / 日期分组,库存支持按仓库分组;
  • 索引优化:高频字段(编码、日期、车间 / 仓库)创建索引,查询效率提升 80%+。

四、部署与使用

1. 环境依赖

  • .NET Framework 4.7.2+ 或 .NET 6/7(Winform);
  • 安装 NuGet 包:Install-Package System.Data.SQLite

2. 使用步骤

  1. 编译运行项目,首次启动自动创建数据库并预置测试数据;
  2. 选择报表类型(工单 / 库存),输入筛选条件,点击「查询」;
  3. 调整排序 / 分组字段,查看不同维度的统计结果;
  4. 点击「新增 / 编辑 / 删除」按钮,手动管理数据。

五、总结

关键点回顾

  1. 数据管理:支持代码预置模拟数据 + 界面手动编辑,兼顾测试与实际使用;
  2. 查询优化:索引 + 动态 SQL + 字段映射,保证 10 万 + 数据高效查询;
  3. 用户体验:数据校验 + 统计展示 + 可视化操作,降低使用门槛;
  4. 本地部署:SQLite 文件数据库,无需额外服务,适配工厂本地场景。

该项目可直接落地 MES/ERP 报表查询场景,支持扩展更多报表类型(如设备、质量),或对接 ERP 系统的外部数据库。

相关推荐
xiaoye37082 小时前
动态代理的使用场景与适用时机
java·数据库·sql
椰猫子2 小时前
数据库MySQL
数据库
哈__2 小时前
告别复杂 SQL 性能瓶颈!金仓智能下推技术的实战解析
数据库·sql
源远流长jerry2 小时前
dpdk19.08编译问题解决方案
数据库·postgresql·sqlserver
微学AI2 小时前
复杂查询中 JOIN 条件下推失败导致的性能瓶颈-金仓数据库
数据库
cyforkk2 小时前
数据库里的隐形守卫:通俗易懂理解 RLS(行级安全)
数据库·安全
zxrhhm2 小时前
Oracle一般而言standby redo日志文件组数要比primary数据库的online redo日志文件组数至少多一个,为什么?
数据库·oracle
ldj20202 小时前
Arthas常用方式
jvm·arthas
小鸡脚来咯2 小时前
SQL常用函数
数据库·sql