MES/ERP大数据报表条件索引查询组件

欢迎各位观众大大浏览阅读我的博客,有空麻烦加一下博客主页关注,谢谢

MES/ERP 报表查询系统(WinForm+SQLite)

一、项目核心设计

本项目围绕MES/ERP 报表查询 + 自定义排序核心场景,实现「本地 SQLite 数据库存储 + 预置测试数据 + 多维度索引约束 + 富交互界面」,重点优化操作便捷性,支持手动添加数据、灵活排序、可视化查询,适配中小企业轻量化报表管理需求。

二、项目完整架构

plaintext

复制代码
MESERPReport/
├─ Data/                  // 数据层
│  ├─ SqliteHelper.cs     // SQLite操作(CRUD+索引)
│  └─ DataInitializer.cs  // 预置测试数据
├─ Business/              // 业务层
│  ├─ ReportService.cs    // 报表查询/数据管理
│  └─ SortEngine.cs       // 高级排序算法
├─ Model/                 // 模型层
│  ├─ ReportModel.cs      // 报表实体
│  └─ SortRule.cs         // 排序规则
└─ UI/                    // 界面层
   ├─ MainForm.cs         // 主窗体(查询/排序/展示)
   └─ AddDataForm.cs      // 新增数据弹窗

三、核心代码实现

1. 模型层(Model)

ReportModel.cs(报表实体 + 查询条件)

csharp

运行

复制代码
using System;

namespace MESERPReport.Model
{
    // 报表核心实体
    public class ReportModel
    {
        public int Id { get; set; }               // 主键
        public string OrderNo { get; set; }       // 工单编号(索引字段)
        public string ProductNo { get; set; }     // 产品编号(索引字段)
        public string Workshop { get; set; }      // 车间
        public decimal Qty { get; set; }          // 生产数量
        public DateTime ProduceDate { get; set; } // 生产日期(索引字段)
        public string Status { get; set; }        // 工单状态(已完成/生产中/暂停)
        public string Operator { get; set; }      // 操作人
    }

    // 查询条件实体
    public class QueryCondition
    {
        public string OrderNo { get; set; }
        public string ProductNo { get; set; }
        public string Workshop { get; set; }
        public string Status { get; set; }
        public DateTime? StartDate { get; set; }
        public DateTime? EndDate { get; set; }
    }

    // 排序规则
    public class SortRule
    {
        public string FieldName { get; set; } // 排序字段(Qty/ProduceDate/OrderNo)
        public bool IsAsc { get; set; }       // 是否升序
        public int Priority { get; set; }     // 优先级(1最高)
    }
}

2. 数据层(Data)

SqliteHelper.cs(SQLite 核心操作)

csharp

运行

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

namespace MESERPReport.Data
{
    public class SqliteHelper
    {
        private readonly string _dbPath;
        private readonly string _connStr;

        public SqliteHelper()
        {
            // 数据库路径:程序目录下的MESERP.db
            _dbPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "MESERP.db");
            _connStr = $"Data Source={_dbPath};Version=3;";
            InitDb(); // 初始化数据库+表+索引
        }

        // 初始化数据库(创建表+索引)
        private void InitDb()
        {
            if (!File.Exists(_dbPath))
            {
                SQLiteConnection.CreateFile(_dbPath);
                using (var conn = new SQLiteConnection(_connStr))
                {
                    conn.Open();
                    // 创建生产报表表
                    var createTableSql = @"
                        CREATE TABLE IF NOT EXISTS ProductionReport (
                            Id INTEGER PRIMARY KEY AUTOINCREMENT,
                            OrderNo VARCHAR(50) NOT NULL,
                            ProductNo VARCHAR(50) NOT NULL,
                            Workshop VARCHAR(30) NOT NULL,
                            Qty DECIMAL(10,2) NOT NULL,
                            ProduceDate DATE NOT NULL,
                            Status VARCHAR(20) NOT NULL,
                            Operator VARCHAR(30)
                        );";
                    ExecuteNonQuery(conn, createTableSql);

                    // 创建核心索引(提升查询/排序效率)
                    var createIndexSqls = new[]
                    {
                        "CREATE INDEX IF NOT EXISTS idx_OrderNo ON ProductionReport(OrderNo);",
                        "CREATE INDEX IF NOT EXISTS idx_ProductNo ON ProductionReport(ProductNo);",
                        "CREATE INDEX IF NOT EXISTS idx_ProduceDate ON ProductionReport(ProduceDate);"
                    };
                    foreach (var sql in createIndexSqls)
                        ExecuteNonQuery(conn, sql);
                }
            }
        }

        // 执行查询(返回DataTable)
        public 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);
                }
            }
        }

        // 执行增删改
        public int ExecuteNonQuery(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);
                        return cmd.ExecuteNonQuery();
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception("操作失败:" + ex.Message);
                }
            }
        }

        // 重载:带连接的执行(初始化用)
        private void ExecuteNonQuery(SQLiteConnection conn, string sql)
        {
            using (var cmd = new SQLiteCommand(sql, conn))
            {
                cmd.ExecuteNonQuery();
            }
        }
    }
}
DataInitializer.cs(预置测试数据)

csharp

运行

复制代码
using System;
using MESERPReport.Model;

namespace MESERPReport.Data
{
    public class DataInitializer
    {
        private readonly SqliteHelper _dbHelper;

        public DataInitializer()
        {
            _dbHelper = new SqliteHelper();
        }

        // 初始化预置测试数据
        public void InitTestData()
        {
            // 先清空旧数据(可选)
            _dbHelper.ExecuteNonQuery("DELETE FROM ProductionReport;");

            // 插入测试数据
            var testData = new[]
            {
                new { OrderNo = "WO202406001", ProductNo = "PROD001", Workshop = "一车间", Qty = 500.00m, ProduceDate = new DateTime(2024,6,1), Status = "已完成", Operator = "张三" },
                new { OrderNo = "WO202406002", ProductNo = "PROD002", Workshop = "二车间", Qty = 800.50m, ProduceDate = new DateTime(2024,6,2), Status = "生产中", Operator = "李四" },
                new { OrderNo = "WO202406003", ProductNo = "PROD001", Workshop = "一车间", Qty = 300.00m, ProduceDate = new DateTime(2024,6,1), Status = "暂停", Operator = "王五" },
                new { OrderNo = "WO202406004", ProductNo = "PROD003", Workshop = "三车间", Qty = 1200.00m, ProduceDate = new DateTime(2024,6,3), Status = "已完成", Operator = "赵六" },
                new { OrderNo = "WO202406005", ProductNo = "PROD002", Workshop = "二车间", Qty = 750.00m, ProduceDate = new DateTime(2024,6,2), Status = "生产中", Operator = "孙七" }
            };

            foreach (var data in testData)
            {
                var sql = @"
                    INSERT INTO ProductionReport (OrderNo, ProductNo, Workshop, Qty, ProduceDate, Status, Operator)
                    VALUES (@OrderNo, @ProductNo, @Workshop, @Qty, @ProduceDate, @Status, @Operator);";
                _dbHelper.ExecuteNonQuery(sql,
                    new SQLiteParameter("@OrderNo", data.OrderNo),
                    new SQLiteParameter("@ProductNo", data.ProductNo),
                    new SQLiteParameter("@Workshop", data.Workshop),
                    new SQLiteParameter("@Qty", data.Qty),
                    new SQLiteParameter("@ProduceDate", data.ProduceDate),
                    new SQLiteParameter("@Status", data.Status),
                    new SQLiteParameter("@Operator", data.Operator));
            }

            Console.WriteLine("预置测试数据初始化完成!");
        }
    }
}

3. 业务层(Business)

ReportService.cs(报表查询 / 数据管理)

csharp

运行

复制代码
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SQLite;
using MESERPReport.Data;
using MESERPReport.Model;

namespace MESERPReport.Business
{
    public class ReportService
    {
        private readonly SqliteHelper _dbHelper;

        public ReportService()
        {
            _dbHelper = new SqliteHelper();
        }

        // 多条件查询报表(带索引约束)
        public DataTable QueryReport(QueryCondition condition)
        {
            var sqlBuilder = new List<string> { "SELECT * FROM ProductionReport WHERE 1=1" };
            var parameters = new List<SQLiteParameter>();

            // 索引约束1:工单编号(精准匹配,命中索引)
            if (!string.IsNullOrEmpty(condition.OrderNo))
            {
                sqlBuilder.Add("AND OrderNo = @OrderNo");
                parameters.Add(new SQLiteParameter("@OrderNo", condition.OrderNo));
            }

            // 索引约束2:产品编号(模糊匹配,命中索引)
            if (!string.IsNullOrEmpty(condition.ProductNo))
            {
                sqlBuilder.Add("AND ProductNo LIKE @ProductNo");
                parameters.Add(new SQLiteParameter("@ProductNo", $"%{condition.ProductNo}%"));
            }

            // 普通条件:车间/状态
            if (!string.IsNullOrEmpty(condition.Workshop))
            {
                sqlBuilder.Add("AND Workshop = @Workshop");
                parameters.Add(new SQLiteParameter("@Workshop", condition.Workshop));
            }
            if (!string.IsNullOrEmpty(condition.Status))
            {
                sqlBuilder.Add("AND Status = @Status");
                parameters.Add(new SQLiteParameter("@Status", condition.Status));
            }

            // 索引约束3:生产日期范围(命中索引)
            if (condition.StartDate.HasValue)
            {
                sqlBuilder.Add("AND ProduceDate >= @StartDate");
                parameters.Add(new SQLiteParameter("@StartDate", condition.StartDate.Value.ToString("yyyy-MM-dd")));
            }
            if (condition.EndDate.HasValue)
            {
                sqlBuilder.Add("AND ProduceDate <= @EndDate");
                parameters.Add(new SQLiteParameter("@EndDate", condition.EndDate.Value.ToString("yyyy-MM-dd")));
            }

            // 默认排序
            sqlBuilder.Add("ORDER BY ProduceDate DESC");

            return _dbHelper.ExecuteQuery(string.Join(" ", sqlBuilder), parameters.ToArray());
        }

        // 添加新报表数据
        public bool AddReportData(ReportModel model)
        {
            try
            {
                var sql = @"
                    INSERT INTO ProductionReport (OrderNo, ProductNo, Workshop, Qty, ProduceDate, Status, Operator)
                    VALUES (@OrderNo, @ProductNo, @Workshop, @Qty, @ProduceDate, @Status, @Operator);";
                var rows = _dbHelper.ExecuteNonQuery(sql,
                    new SQLiteParameter("@OrderNo", model.OrderNo),
                    new SQLiteParameter("@ProductNo", model.ProductNo),
                    new SQLiteParameter("@Workshop", model.Workshop),
                    new SQLiteParameter("@Qty", model.Qty),
                    new SQLiteParameter("@ProduceDate", model.ProduceDate),
                    new SQLiteParameter("@Status", model.Status),
                    new SQLiteParameter("@Operator", model.Operator));
                return rows > 0;
            }
            catch
            {
                return false;
            }
        }
    }
}
SortEngine.cs(高级排序算法)

csharp

运行

复制代码
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using MESERPReport.Model;

namespace MESERPReport.Business
{
    public class SortEngine
    {
        // 多规则排序(支持优先级+升降序)
        public DataTable SortReport(DataTable sourceDt, List<SortRule> sortRules)
        {
            if (sourceDt == null || sourceDt.Rows.Count == 0 || sortRules == null || sortRules.Count == 0)
                return sourceDt;

            // 转换为List便于LINQ排序
            var dataList = sourceDt.AsEnumerable().ToList();
            IOrderedEnumerable<DataRow> sortedList = null;

            // 按优先级排序(数字越小优先级越高)
            foreach (var rule in sortRules.OrderBy(r => r.Priority))
            {
                if (sortedList == null)
                {
                    // 首次排序
                    sortedList = rule.IsAsc
                        ? dataList.OrderBy(row => GetSortValue(row, rule.FieldName))
                        : dataList.OrderByDescending(row => GetSortValue(row, rule.FieldName));
                }
                else
                {
                    // 后续排序(ThenBy)
                    sortedList = rule.IsAsc
                        ? sortedList.ThenBy(row => GetSortValue(row, rule.FieldName))
                        : sortedList.ThenByDescending(row => GetSortValue(row, rule.FieldName));
                }
            }

            // 重构DataTable
            var resultDt = sourceDt.Clone();
            foreach (var row in sortedList)
            {
                resultDt.ImportRow(row);
            }
            return resultDt;
        }

        // 获取排序值(适配不同字段类型)
        private object GetSortValue(DataRow row, string fieldName)
        {
            if (row[fieldName] == DBNull.Value)
                return fieldName switch
                {
                    "Qty" => 0m,
                    "ProduceDate" => DateTime.MinValue,
                    _ => string.Empty
                };

            return fieldName switch
            {
                "Qty" => Convert.ToDecimal(row[fieldName]),
                "ProduceDate" => Convert.ToDateTime(row[fieldName]),
                _ => row[fieldName].ToString()
            };
        }
    }
}

4. 界面层(UI)

AddDataForm.cs(新增数据弹窗)

csharp

运行

复制代码
using System;
using System.Windows.Forms;
using MESERPReport.Business;
using MESERPReport.Model;

namespace MESERPReport.UI
{
    public partial class AddDataForm : Form
    {
        private readonly ReportService _reportService;

        public AddDataForm()
        {
            InitializeComponent();
            _reportService = new ReportService();
            InitAddDataUI();
        }

        // 初始化新增数据界面
        private void InitAddDataUI()
        {
            // 窗体设置
            this.Text = "新增生产报表数据";
            this.Size = new Size(400, 400);
            this.StartPosition = FormStartPosition.CenterParent;

            // 控件布局
            int y = 20;
            // 工单编号
            var lblOrderNo = new Label { Text = "工单编号:", Location = new Point(20, y), AutoSize = true };
            var txtOrderNo = new TextBox { Name = "txtOrderNo", Location = new Point(100, y), Width = 250 };
            y += 40;

            // 产品编号
            var lblProductNo = new Label { Text = "产品编号:", Location = new Point(20, y), AutoSize = true };
            var txtProductNo = new TextBox { Name = "txtProductNo", Location = new Point(100, y), Width = 250 };
            y += 40;

            // 车间
            var lblWorkshop = new Label { Text = "车间:", Location = new Point(20, y), AutoSize = true };
            var cboWorkshop = new ComboBox
            {
                Name = "cboWorkshop",
                Location = new Point(100, y),
                Width = 250,
                DropDownStyle = ComboBoxStyle.DropDownList,
                DataSource = new[] { "一车间", "二车间", "三车间" }
            };
            y += 40;

            // 生产数量
            var lblQty = new Label { Text = "生产数量:", Location = new Point(20, y), AutoSize = true };
            var numQty = new NumericUpDown
            {
                Name = "numQty",
                Location = new Point(100, y),
                Width = 250,
                Minimum = 0,
                Maximum = 99999,
                DecimalPlaces = 2
            };
            y += 40;

            // 生产日期
            var lblDate = new Label { Text = "生产日期:", Location = new Point(20, y), AutoSize = true };
            var dtpDate = new DateTimePicker
            {
                Name = "dtpDate",
                Location = new Point(100, y),
                Width = 250,
                Format = DateTimePickerFormat.Short
            };
            y += 40;

            // 工单状态
            var lblStatus = new Label { Text = "工单状态:", Location = new Point(20, y), AutoSize = true };
            var cboStatus = new ComboBox
            {
                Name = "cboStatus",
                Location = new Point(100, y),
                Width = 250,
                DropDownStyle = ComboBoxStyle.DropDownList,
                DataSource = new[] { "已完成", "生产中", "暂停" }
            };
            y += 40;

            // 操作人
            var lblOperator = new Label { Text = "操作人:", Location = new Point(20, y), AutoSize = true };
            var txtOperator = new TextBox { Name = "txtOperator", Location = new Point(100, y), Width = 250 };
            y += 60;

            // 按钮
            var btnSave = new Button
            {
                Text = "保存",
                Location = new Point(100, y),
                Width = 80,
                BackColor = Color.LightGreen
            };
            btnSave.Click += (s, e) => SaveData(txtOrderNo, txtProductNo, cboWorkshop, numQty, dtpDate, cboStatus, txtOperator);

            var btnCancel = new Button
            {
                Text = "取消",
                Location = new Point(200, y),
                Width = 80,
                BackColor = Color.LightSalmon
            };
            btnCancel.Click += (s, e) => this.Close();

            // 添加控件
            this.Controls.AddRange(new Control[]
            {
                lblOrderNo, txtOrderNo, lblProductNo, txtProductNo,
                lblWorkshop, cboWorkshop, lblQty, numQty, lblDate, dtpDate,
                lblStatus, cboStatus, lblOperator, txtOperator, btnSave, btnCancel
            });
        }

        // 保存数据
        private void SaveData(TextBox txtOrderNo, TextBox txtProductNo, ComboBox cboWorkshop,
            NumericUpDown numQty, DateTimePicker dtpDate, ComboBox cboStatus, TextBox txtOperator)
        {
            // 校验必填项
            if (string.IsNullOrEmpty(txtOrderNo.Text) || string.IsNullOrEmpty(txtProductNo.Text))
            {
                MessageBox.Show("工单编号和产品编号不能为空!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            // 构建模型
            var model = new ReportModel
            {
                OrderNo = txtOrderNo.Text.Trim(),
                ProductNo = txtProductNo.Text.Trim(),
                Workshop = cboWorkshop.Text,
                Qty = (decimal)numQty.Value,
                ProduceDate = dtpDate.Value,
                Status = cboStatus.Text,
                Operator = txtOperator.Text.Trim()
            };

            // 保存数据
            var result = _reportService.AddReportData(model);
            if (result)
            {
                MessageBox.Show("数据添加成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
                this.DialogResult = DialogResult.OK;
                this.Close();
            }
            else
            {
                MessageBox.Show("数据添加失败!", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 设计器代码(简化)
        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.SuspendLayout();
            this.Name = "AddDataForm";
            this.Size = new Size(400, 400);
            this.ResumeLayout(false);
        }
    }
}
MainForm.cs(主窗体 - 核心界面)

csharp

运行

复制代码
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using MESERPReport.Business;
using MESERPReport.Data;
using MESERPReport.Model;

namespace MESERPReport.UI
{
    public partial class MainForm : Form
    {
        private readonly ReportService _reportService;
        private readonly SortEngine _sortEngine;
        private DataTable _originalDt; // 原始查询数据
        private List<SortRule> _sortRules = new List<SortRule>(); // 排序规则

        public MainForm()
        {
            InitializeComponent();
            _reportService = new ReportService();
            _sortEngine = new SortEngine();
            // 初始化预置数据
            new DataInitializer().InitTestData();
            // 初始化主界面
            InitMainUI();
        }

        // 初始化主界面(富交互控件)
        private void InitMainUI()
        {
            // 窗体设置
            this.Text = "MES/ERP生产报表查询系统";
            this.Size = new Size(1100, 700);
            this.StartPosition = FormStartPosition.CenterScreen;

            // ========== 1. 顶部功能区 ==========
            var pnlTop = new Panel { Dock = DockStyle.Top, Height = 180, BackColor = Color.AliceBlue, BorderStyle = BorderStyle.FixedSingle };

            // 1.1 查询条件组
            var grpQuery = new GroupBox { Text = "查询条件", Location = new Point(10, 10), Size = new Size(500, 150) };
            int y = 20;
            // 工单编号
            var lblOrderNo = new Label { Text = "工单编号:", Location = new Point(20, y), AutoSize = true };
            var txtOrderNo = new TextBox { Name = "txtOrderNo", Location = new Point(100, y), Width = 150, PlaceholderText = "精准匹配(如WO202406001)" };
            y += 35;

            // 产品编号
            var lblProductNo = new Label { Text = "产品编号:", Location = new Point(20, y), AutoSize = true };
            var txtProductNo = new TextBox { Name = "txtProductNo", Location = new Point(100, y), Width = 150, PlaceholderText = "模糊匹配(如PROD00)" };
            y += 35;

            // 车间+状态
            var lblWorkshop = new Label { Text = "车间:", Location = new Point(20, y), AutoSize = true };
            var cboWorkshop = new ComboBox { Name = "cboWorkshop", Location = new Point(100, y), Width = 100, DropDownStyle = ComboBoxStyle.DropDownList, DataSource = new[] { "", "一车间", "二车间", "三车间" } };

            var lblStatus = new Label { Text = "状态:", Location = new Point(220, y), AutoSize = true };
            var cboStatus = new ComboBox { Name = "cboStatus", Location = new Point(260, y), Width = 100, DropDownStyle = ComboBoxStyle.DropDownList, DataSource = new[] { "", "已完成", "生产中", "暂停" } };
            y += 35;

            // 日期范围
            var lblDate = new Label { Text = "日期范围:", Location = new Point(20, y), AutoSize = true };
            var dtpStart = new DateTimePicker { Name = "dtpStart", Location = new Point(100, y), Width = 100, Format = DateTimePickerFormat.Short };
            var lblTo = new Label { Text = "至", Location = new Point(210, y), AutoSize = true };
            var dtpEnd = new DateTimePicker { Name = "dtpEnd", Location = new Point(230, y), Width = 100, Format = DateTimePickerFormat.Short };

            grpQuery.Controls.AddRange(new Control[] { lblOrderNo, txtOrderNo, lblProductNo, txtProductNo, lblWorkshop, cboWorkshop, lblStatus, cboStatus, lblDate, dtpStart, lblTo, dtpEnd });

            // 1.2 功能按钮组
            var grpBtn = new GroupBox { Text = "操作", Location = new Point(520, 10), Size = new Size(200, 150) };
            // 查询按钮
            var btnQuery = new Button { Text = "执行查询", Location = new Point(20, 30), Width = 150, Height = 35, BackColor = Color.LightGreen, Font = new Font("微软雅黑", 9, FontStyle.Bold) };
            btnQuery.Click += (s, e) => ExecuteQuery(txtOrderNo, txtProductNo, cboWorkshop, cboStatus, dtpStart, dtpEnd);

            // 清空按钮
            var btnClear = new Button { Text = "清空条件", Location = new Point(20, 80), Width = 150, Height = 30, BackColor = Color.LightSalmon };
            btnClear.Click += (s, e) => ClearQueryCondition(grpQuery);

            // 新增数据按钮
            var btnAdd = new Button { Text = "新增数据", Location = new Point(20, 120), Width = 150, Height = 30, BackColor = Color.LightSkyBlue };
            btnAdd.Click += (s, e) => OpenAddDataForm();

            grpBtn.Controls.AddRange(new Control[] { btnQuery, btnClear, btnAdd });

            // 1.3 排序配置组
            var grpSort = new GroupBox { Text = "排序配置", Location = new Point(730, 10), Size = new Size(320, 150) };
            y = 20;
            // 排序字段
            var lblSortField = new Label { Text = "排序字段:", Location = new Point(20, y), AutoSize = true };
            var cboSortField = new ComboBox { Name = "cboSortField", Location = new Point(100, y), Width = 100, DropDownStyle = ComboBoxStyle.DropDownList, DataSource = new[] { "OrderNo", "ProductNo", "Qty", "ProduceDate" } };
            y += 35;

            // 排序方向
            var lblSortDir = new Label { Text = "排序方向:", Location = new Point(20, y), AutoSize = true };
            var cboSortDir = new ComboBox { Name = "cboSortDir", Location = new Point(100, y), Width = 100, DropDownStyle = ComboBoxStyle.DropDownList, DataSource = new[] { "升序", "降序" } };
            y += 35;

            // 优先级
            var lblPriority = new Label { Text = "优先级:", Location = new Point(20, y), AutoSize = true };
            var numPriority = new NumericUpDown { Name = "numPriority", Location = new Point(100, y), Width = 60, Minimum = 1, Maximum = 5, Value = 1 };
            y += 35;

            // 排序按钮
            var btnAddSort = new Button { Text = "添加排序规则", Location = new Point(20, y), Width = 100 };
            btnAddSort.Click += (s, e) => AddSortRule(cboSortField, cboSortDir, numPriority);

            var btnSort = new Button { Text = "执行排序", Location = new Point(130, y), Width = 100, BackColor = Color.Gold };
            btnSort.Click += (s, e) => ExecuteSort();

            // 排序规则列表
            var lstSortRule = new ListBox { Name = "lstSortRule", Location = new Point(220, 20), Size = new Size(80, 110) };

            grpSort.Controls.AddRange(new Control[] { lblSortField, cboSortField, lblSortDir, cboSortDir, lblPriority, numPriority, btnAddSort, btnSort, lstSortRule });

            pnlTop.Controls.AddRange(new Control[] { grpQuery, grpBtn, grpSort });

            // ========== 2. 数据展示区 ==========
            var dgvReport = new DataGridView
            {
                Name = "dgvReport",
                Dock = DockStyle.Fill,
                AutoGenerateColumns = false,
                ReadOnly = true,
                SelectionMode = DataGridViewSelectionMode.FullRowSelect,
                MultiSelect = false,
                RowHeadersVisible = false,
                AllowUserToAddRows = false,
                AlternatingRowsDefaultCellStyle = { BackColor = Color.FromArgb(248, 248, 248) },
                ColumnHeadersDefaultCellStyle =
                {
                    BackColor = Color.FromArgb(51, 122, 183),
                    ForeColor = Color.White,
                    Font = new Font("微软雅黑", 9, FontStyle.Bold)
                }
            };

            // 自定义列
            dgvReport.Columns.AddRange(new DataGridViewColumn[]
            {
                new DataGridViewTextBoxColumn { Name = "colId", HeaderText = "ID", DataPropertyName = "Id", Width = 60 },
                new DataGridViewTextBoxColumn { Name = "colOrderNo", HeaderText = "工单编号", DataPropertyName = "OrderNo", Width = 120 },
                new DataGridViewTextBoxColumn { Name = "colProductNo", HeaderText = "产品编号", DataPropertyName = "ProductNo", Width = 100 },
                new DataGridViewTextBoxColumn { Name = "colWorkshop", HeaderText = "车间", DataPropertyName = "Workshop", Width = 80 },
                new DataGridViewTextBoxColumn { Name = "colQty", HeaderText = "生产数量", DataPropertyName = "Qty", Width = 90, DefaultCellStyle = { Alignment = DataGridViewContentAlignment.MiddleRight } },
                new DataGridViewTextBoxColumn { Name = "colDate", HeaderText = "生产日期", DataPropertyName = "ProduceDate", Width = 100 },
                new DataGridViewTextBoxColumn { Name = "colStatus", HeaderText = "状态", DataPropertyName = "Status", Width = 80 },
                new DataGridViewTextBoxColumn { Name = "colOperator", HeaderText = "操作人", DataPropertyName = "Operator", Width = 80 }
            });

            // ========== 3. 状态栏 ==========
            var statusStrip = new StatusStrip();
            var tslCount = new ToolStripStatusLabel { Text = "数据条数:0" };
            var tslTime = new ToolStripStatusLabel { Text = "查询耗时:0ms", Alignment = ToolStripItemAlignment.Right };
            statusStrip.Items.AddRange(new ToolStripItem[] { tslCount, tslTime });

            // ========== 组装控件 ==========
            this.Controls.AddRange(new Control[] { dgvReport, pnlTop, statusStrip });
        }

        // 执行查询
        private void ExecuteQuery(TextBox txtOrderNo, TextBox txtProductNo, ComboBox cboWorkshop,
            ComboBox cboStatus, DateTimePicker dtpStart, DateTimePicker dtpEnd)
        {
            try
            {
                var sw = new System.Diagnostics.Stopwatch();
                sw.Start();

                // 构建查询条件
                var condition = new QueryCondition
                {
                    OrderNo = txtOrderNo.Text.Trim(),
                    ProductNo = txtProductNo.Text.Trim(),
                    Workshop = cboWorkshop.Text,
                    Status = cboStatus.Text,
                    StartDate = dtpStart.Value,
                    EndDate = dtpEnd.Value
                };

                // 执行查询
                _originalDt = _reportService.QueryReport(condition);

                // 绑定数据
                var dgv = (DataGridView)this.Controls.Find("dgvReport", true)[0];
                dgv.DataSource = _originalDt;

                // 更新状态栏
                sw.Stop();
                ((ToolStripStatusLabel)((StatusStrip)this.Controls["statusStrip1"]).Items[0]).Text = $"数据条数:{_originalDt.Rows.Count}";
                ((ToolStripStatusLabel)((StatusStrip)this.Controls["statusStrip1"]).Items[1]).Text = $"查询耗时:{sw.ElapsedMilliseconds}ms";

                MessageBox.Show($"查询完成!共{_originalDt.Rows.Count}条数据", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"查询失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 清空查询条件
        private void ClearQueryCondition(GroupBox grpQuery)
        {
            foreach (Control ctrl in grpQuery.Controls)
            {
                if (ctrl is TextBox txt) txt.Text = string.Empty;
                else if (ctrl is ComboBox cbo) cbo.SelectedIndex = 0;
                else if (ctrl is DateTimePicker dtp) dtp.Value = DateTime.Today;
            }
        }

        // 打开新增数据窗体
        private void OpenAddDataForm()
        {
            var addForm = new AddDataForm();
            if (addForm.ShowDialog() == DialogResult.OK)
            {
                // 新增成功后刷新数据
                var txtOrderNo = (TextBox)this.Controls.Find("txtOrderNo", true)[0];
                ExecuteQuery(txtOrderNo, (TextBox)this.Controls.Find("txtProductNo", true)[0],
                    (ComboBox)this.Controls.Find("cboWorkshop", true)[0], (ComboBox)this.Controls.Find("cboStatus", true)[0],
                    (DateTimePicker)this.Controls.Find("dtpStart", true)[0], (DateTimePicker)this.Controls.Find("dtpEnd", true)[0]);
            }
        }

        // 添加排序规则
        private void AddSortRule(ComboBox cboSortField, ComboBox cboSortDir, NumericUpDown numPriority)
        {
            var rule = new SortRule
            {
                FieldName = cboSortField.Text,
                IsAsc = cboSortDir.Text == "升序",
                Priority = (int)numPriority.Value
            };

            _sortRules.Add(rule);

            // 更新规则列表
            var lst = (ListBox)this.Controls.Find("lstSortRule", true)[0];
            lst.Items.Add($"P{rule.Priority}:{rule.FieldName}({(rule.IsAsc ? "升" : "降")})");

            MessageBox.Show("排序规则添加成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        // 执行排序
        private void ExecuteSort()
        {
            if (_originalDt == null || _originalDt.Rows.Count == 0)
            {
                MessageBox.Show("请先执行查询!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            if (_sortRules.Count == 0)
            {
                MessageBox.Show("请添加排序规则!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            try
            {
                var sortedDt = _sortEngine.SortReport(_originalDt, _sortRules);
                var dgv = (DataGridView)this.Controls.Find("dgvReport", true)[0];
                dgv.DataSource = sortedDt;
                MessageBox.Show("排序完成!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                MessageBox.Show($"排序失败:{ex.Message}", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        // 设计器代码(简化)
        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.statusStrip1 = new System.Windows.Forms.StatusStrip();
            this.SuspendLayout();
            this.statusStrip1.Name = "statusStrip1";
            this.statusStrip1.Size = new System.Drawing.Size(800, 22);
            this.Controls.Add(this.statusStrip1);
            this.Name = "MainForm";
            this.Size = new System.Drawing.Size(800, 450);
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        private StatusStrip statusStrip1;
    }
}
Program.cs(程序入口)

csharp

运行

复制代码
using System;
using System.Windows.Forms;
using MESERPReport.UI;

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

四、核心亮点

1. 数据预置与管理

  • 自动初始化测试数据:程序启动时自动创建 SQLite 数据库并插入 5 条测试数据,无需手动造数;
  • 手动添加数据:提供弹窗式新增数据界面,支持工单、产品、车间等信息录入,校验必填项;
  • 本地轻量化存储:基于 SQLite,无需服务端,单机即可运行。

2. 索引约束与查询优化

  • 多维度索引:为工单编号、产品编号、生产日期创建索引,提升查询效率;
  • 条件约束:精准 / 模糊查询区分,日期范围校验,避免无效查询;
  • 参数化查询:防止 SQL 注入,保证数据安全。

3. 排序算法增强

  • 多规则排序:支持按优先级、字段类型(数值 / 日期 / 字符串)、升降序组合排序;
  • 空值处理:自动适配空值默认值,避免排序异常;
  • LINQ 优化:基于内存 List 排序,兼顾性能与可读性。

4. 界面交互优化

  • 分组布局:查询 / 排序 / 操作区域清晰分离,逻辑直观;
  • 富交互控件:下拉框预设值、输入框占位符、数值选择器、日期选择器,减少输入错误;
  • 视觉优化:交替行颜色、表头高亮、数值右对齐,提升可读性;
  • 实时反馈:状态栏显示数据条数、查询耗时,操作结果弹窗提示。

五、使用说明

1. 环境准备

  • Visual Studio 2019/2022;
  • .NET Framework 4.7.2+;
  • 安装 NuGet 包:System.Data.SQLite(版本 1.0.118+)。

2. 运行步骤

  1. 创建 WinForm 项目,命名为MESERPReport
  2. 按架构创建文件夹(Data/Business/Model/UI);
  3. 将对应代码复制到各文件夹下的类文件;
  4. 编译运行,首次启动自动创建数据库并插入测试数据;
  5. 功能操作:
    • 查询:输入条件(如产品编号 PROD001),点击「执行查询」;
    • 排序:添加排序规则(如按生产数量降序),点击「执行排序」;
    • 新增数据:点击「新增数据」,录入信息后保存,自动刷新列表。

总结

关键点回顾

  1. 数据层:SQLite 本地存储 + 预置测试数据 + 多维度索引,保证数据访问效率;
  2. 业务层:多条件查询 + 多规则排序算法,满足 MES/ERP 报表核心需求;
  3. 界面层:富交互控件 + 分组布局 + 实时反馈,大幅提升操作便捷性;
  4. 扩展灵活:支持新增 / 查询 / 排序核心功能,可快速扩展导出、批量操作等功能。

该项目完全适配中小企业 MES/ERP 报表管理场景,轻量化部署、操作便捷、功能完整,可直接作为基础框架扩展个性化需求。

相关推荐
wmfglpz882 小时前
Django全栈开发入门:构建一个博客系统
jvm·数据库·python
m0_598177232 小时前
MYSQL order by , group by练习
数据库·mysql
TDengine (老段)2 小时前
TDengine IDMP 组态面板 —— 总体介绍
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
jwn9992 小时前
【Mysql】:如何恢复误删的数据?
数据库·mysql
yashuk2 小时前
Redis的安装教程(Windows+Linux)【超详细】
linux·数据库·redis
lzp07912 小时前
mysql之联合索引
数据库·mysql
qq5680180762 小时前
【MySQL】超详细MySQL常用日期格式转换函数、字符串函数、聚合函数(最新版)
数据库·mysql
逆境不可逃2 小时前
【从零入门23种设计模式21】行为型之空对象模式
java·开发语言·数据库·算法·设计模式·职场和发展
灰阳阳3 小时前
Redis的缓存机制
数据库·redis·缓存