根据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. 使用步骤
- 编译运行项目,首次启动自动创建数据库并预置测试数据;
- 选择报表类型(工单 / 库存),输入筛选条件,点击「查询」;
- 调整排序 / 分组字段,查看不同维度的统计结果;
- 点击「新增 / 编辑 / 删除」按钮,手动管理数据。
五、总结
关键点回顾
- 数据管理:支持代码预置模拟数据 + 界面手动编辑,兼顾测试与实际使用;
- 查询优化:索引 + 动态 SQL + 字段映射,保证 10 万 + 数据高效查询;
- 用户体验:数据校验 + 统计展示 + 可视化操作,降低使用门槛;
- 本地部署:SQLite 文件数据库,无需额外服务,适配工厂本地场景。
该项目可直接落地 MES/ERP 报表查询场景,支持扩展更多报表类型(如设备、质量),或对接 ERP 系统的外部数据库。