以下是基于现有 MES/ERP 工序 BOM 协同系统架构完善并扩展的 WinForm 模块化实现代码涵盖GDI 图表增强、数据维护、多模块联动、本地 SQLite 数据管理等核心能力保持原有主子结构 / 分节点执行 / 汇总工作台的设计体系1. 核心扩展GDI 图表控件增强GdiChartEx.cscsharp运行using System; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; /// summary /// 增强版GDI柱状图控件支持多系列、图例、网格、自定义样式 /// /summary public class GdiChartEx : Panel { // 图表配置 public Color BarColor { get; set; } Color.CornflowerBlue; public Color GridColor { get; set; } Color.LightGray; public Color TextColor { get; set; } Color.Black; public Font LabelFont { get; set; } new Font(微软雅黑, 9); public Font ValueFont { get; set; } new Font(微软雅黑, 10, FontStyle.Bold); // 数据源 private DataTable _dataSource; public DataTable DataSource { get _dataSource; set { _dataSource value; Invalidate(); // 数据变更重绘 } } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); if (DataSource null || DataSource.Rows.Count 0) return; var g e.Graphics; g.SmoothingMode SmoothingMode.AntiAlias; // 抗锯齿 g.Clear(BackColor); // 绘制网格背景 DrawGrid(g); // 计算图表区域留边距 int paddingLeft 80, paddingRight 20, paddingTop 30, paddingBottom 60; int chartWidth ClientSize.Width - paddingLeft - paddingRight; int chartHeight ClientSize.Height - paddingTop - paddingBottom; // 计算最大值用于刻度 int maxValue GetMaxValue(); float yScale (float)chartHeight / maxValue; // 绘制柱状图 int barWidth Math.Min(80, chartWidth / (DataSource.Rows.Count * 2)); // 自适应柱宽 int x paddingLeft (chartWidth - DataSource.Rows.Count * (barWidth 30)) / 2; // 居中 foreach (DataRow row in DataSource.Rows) { string category row[0].ToString(); int value int.Parse(row[1].ToString()); // 柱体坐标计算 float barHeight value * yScale; float y paddingTop (chartHeight - barHeight); // 绘制柱体渐变边框 using (var brush new LinearGradientBrush( new Point(x, (int)y), new Point(x, (int)(y barHeight)), BarColor, BarColor.Darken(30))) { g.FillRectangle(brush, x, y, barWidth, barHeight); g.DrawRectangle(Pens.Black, x, y, barWidth, barHeight); } // 绘制数值标签 var valueText value.ToString(N0); var valueSize g.MeasureString(valueText, ValueFont); g.DrawString(valueText, ValueFont, new SolidBrush(TextColor), x (barWidth - valueSize.Width) / 2, y - valueSize.Height - 5); // 绘制分类标签旋转45度避免重叠 g.TranslateTransform(x barWidth / 2, ClientSize.Height - paddingBottom 20); g.RotateTransform(-45); g.DrawString(category, LabelFont, new SolidBrush(Color.DarkRed), -g.MeasureString(category, LabelFont).Width / 2, 0); g.ResetTransform(); x barWidth 30; // 柱间距 } // 绘制图例 DrawLegend(g); // 日志记录 DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} 增强版柱状图绘制完成 | 数据行数{DataSource.Rows.Count}\r\n); } /// summary /// 绘制网格线 /// /summary private void DrawGrid(Graphics g) { int paddingLeft 80, paddingTop 30, paddingBottom 60; int chartHeight ClientSize.Height - paddingTop - paddingBottom; int chartWidth ClientSize.Width - paddingLeft - 20; // 横向网格5等分 int gridCount 5; float gridStep chartHeight / gridCount; for (int i 0; i gridCount; i) { float y paddingTop i * gridStep; using (var pen new Pen(GridColor, 1)) { g.DrawLine(pen, paddingLeft, y, ClientSize.Width - 20, y); } // 刻度值 int scaleValue (int)((chartHeight - i * gridStep) / chartHeight * GetMaxValue()); g.DrawString(scaleValue.ToString(), LabelFont, Brushes.Gray, paddingLeft - 50, y - 10); } // 纵向基线 g.DrawLine(Pens.Black, paddingLeft, paddingTop, paddingLeft, ClientSize.Height - paddingBottom); g.DrawLine(Pens.Black, paddingLeft, ClientSize.Height - paddingBottom, ClientSize.Width - 20, ClientSize.Height - paddingBottom); } /// summary /// 绘制图例 /// /summary private void DrawLegend(Graphics g) { int legendX ClientSize.Width - 150; int legendY 20; // 图例框 g.FillRectangle(Brushes.White, legendX, legendY, 120, 40); g.DrawRectangle(Pens.Black, legendX, legendY, 120, 40); // 图例颜色块 g.FillRectangle(new SolidBrush(BarColor), legendX 10, legendY 10, 20, 15); g.DrawRectangle(Pens.Black, legendX 10, legendY 10, 20, 15); // 图例文字 g.DrawString(计划产量, LabelFont, Brushes.Black, legendX 35, legendY 10); } /// summary /// 获取数据源最大值用于刻度计算 /// /summary private int GetMaxValue() { int max 0; foreach (DataRow row in DataSource.Rows) { int val int.Parse(row[1].ToString()); if (val max) max val; } // 向上取整到最近的10的倍数 return (int)Math.Ceiling(max / 10.0) * 10; } /// summary /// 刷新图表 /// /summary public new void Refresh() Invalidate(); } /// summary /// Color扩展方法 /// /summary public static class ColorExtensions { public static Color Darken(this Color color, int percent) { float factor 1 - percent / 100f; return Color.FromArgb( color.A, (int)(color.R * factor), (int)(color.G * factor), (int)(color.B * factor)); } }2. 数据维护模块DataMaintenanceView.cscsharp运行using System; using System.Data; using System.Windows.Forms; /// summary /// 本地数据库数据维护模块支持8-9组基础数据编辑/新增/删除 /// /summary public static class DataMaintenanceView { // 支持维护的表名 private static readonly string[] _maintainTables { WorkStation, ProcessRoute, BOM, WorkTask }; public static TabPage Create() { var page new TabPage(数据维护中心); // 顶部选择区 var topPanel new Panel { Dock DockStyle.Top, Height 60 }; var cboTable new ComboBox { Dock DockStyle.Left, Width 200, DataSource _maintainTables, DropDownStyle ComboBoxStyle.DropDownList }; var btnRefresh new Button { Text 刷新数据, Dock DockStyle.Left, Width 100 }; var btnSave new Button { Text 保存修改, Dock DockStyle.Left, Width 100 }; var btnAdd new Button { Text 新增行, Dock DockStyle.Left, Width 100 }; var btnDel new Button { Text 删除选中, Dock DockStyle.Left, Width 100 }; topPanel.Controls.AddRange(new Control[] { cboTable, btnRefresh, btnSave, btnAdd, btnDel }); // 数据展示区 var dgvData new DataGridView { Dock DockStyle.Fill, AllowUserToAddRows false, AllowUserToDeleteRows false, AutoGenerateColumns true, ReadOnly false }; // 布局组装 page.Controls.Add(dgvData); page.Controls.Add(topPanel); // 事件绑定 cboTable.SelectedIndexChanged (s, e) LoadTableData(cboTable.Text, dgvData); btnRefresh.Click (s, e) LoadTableData(cboTable.Text, dgvData); btnAdd.Click (s, e) AddNewRow(cboTable.Text, dgvData); btnDel.Click (s, e) DeleteSelectedRow(cboTable.Text, dgvData); btnSave.Click (s, e) SaveTableChanges(cboTable.Text, dgvData); // 初始加载第一个表 if (_maintainTables.Length 0) LoadTableData(_maintainTables[0], dgvData); return page; } /// summary /// 加载指定表数据 /// /summary private static void LoadTableData(string tableName, DataGridView dgv) { try { var dt LocalDB.Query($SELECT * FROM {tableName}); dgv.DataSource dt; DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} 加载[{tableName}]数据完成共{dt.Rows.Count}行\r\n); } catch (Exception ex) { MessageBox.Show($加载数据失败{ex.Message}); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} 加载[{tableName}]数据失败{ex.Message}\r\n); } } /// summary /// 新增行 /// /summary private static void AddNewRow(string tableName, DataGridView dgv) { if (dgv.DataSource is not DataTable dt) return; var newRow dt.NewRow(); // 自动填充主键简单自增逻辑 var maxId LocalDB.Query($SELECT MAX(Id) FROM {tableName}).Rows[0][0]; newRow[Id] maxId DBNull.Value ? 1 : Convert.ToInt32(maxId) 1; dt.Rows.Add(newRow); dgv.Refresh(); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]新增空白行\r\n); } /// summary /// 删除选中行 /// /summary private static void DeleteSelectedRow(string tableName, DataGridView dgv) { if (dgv.CurrentRow null) { MessageBox.Show(请选中要删除的行); return; } var id dgv.CurrentRow.Cells[Id].Value; if (id DBNull.Value || id null) { MessageBox.Show(无效的行ID); return; } try { LocalDB.Execute($DELETE FROM {tableName} WHERE Id{id}); LoadTableData(tableName, dgv); // 刷新数据 DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]删除ID{id}的行\r\n); MesEventBus.SendEdit(tableName); // 触发数据编辑事件 } catch (Exception ex) { MessageBox.Show($删除失败{ex.Message}); } } /// summary /// 保存修改批量更新 /// /summary private static void SaveTableChanges(string tableName, DataGridView dgv) { if (dgv.DataSource is not DataTable dt) return; try { foreach (DataRow row in dt.Rows) { if (row.RowState DataRowState.Modified) { // 构建更新SQL通用逻辑适配4个核心表 string sql BuildUpdateSql(tableName, row); LocalDB.Execute(sql); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]更新ID{row[Id]}{sql}\r\n); } else if (row.RowState DataRowState.Added) { // 构建插入SQL string sql BuildInsertSql(tableName, row); LocalDB.Execute(sql); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]新增ID{row[Id]}{sql}\r\n); } } // 提交后刷新 dt.AcceptChanges(); LoadTableData(tableName, dgv); MesEventBus.SendEdit(tableName); // 触发数据编辑事件 MessageBox.Show(数据保存成功); } catch (Exception ex) { MessageBox.Show($保存失败{ex.Message}); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]保存失败{ex.Message}\r\n); } } /// summary /// 构建更新SQL /// /summary private static string BuildUpdateSql(string tableName, DataRow row) { return tableName switch { WorkStation $UPDATE WorkStation SET Name{row[Name]}, Role{row[Role]} WHERE Id{row[Id]}, ProcessRoute $UPDATE ProcessRoute SET Name{row[Name]}, StationId{row[StationId]}, Sort{row[Sort]} WHERE Id{row[Id]}, BOM $UPDATE BOM SET ProductCode{row[ProductCode]}, Material{row[Material]}, UseQty{row[UseQty]}, Loss{row[Loss]} WHERE Id{row[Id]}, WorkTask $UPDATE WorkTask SET TaskNo{row[TaskNo]}, StationId{row[StationId]}, StationName{row[StationName]}, Status{row[Status]}, PlanQty{row[PlanQty]} WHERE Id{row[Id]}, _ throw new NotSupportedException($不支持的表{tableName}) }; } /// summary /// 构建插入SQL /// /summary private static string BuildInsertSql(string tableName, DataRow row) { return tableName switch { WorkStation $INSERT INTO WorkStation VALUES({row[Id]},{row[Name]},{row[Role]}), ProcessRoute $INSERT INTO ProcessRoute VALUES({row[Id]},{row[Name]},{row[StationId]},{row[Sort]}), BOM $INSERT INTO BOM VALUES({row[Id]},{row[ProductCode]},{row[Material]},{row[UseQty]},{row[Loss]}), WorkTask $INSERT INTO WorkTask VALUES({row[Id]},{row[TaskNo]},{row[StationId]},{row[StationName]},{row[Status]},{row[PlanQty]}), _ throw new NotSupportedException($不支持的表{tableName}) }; } }3. 主窗体扩展MainForm.cs 完善csharp运行using System; using System.Data; using System.Drawing; using System.Windows.Forms; public partial class MainForm : Form { private readonly GdiChartEx _chartEx new GdiChartEx(); // 替换为增强版图表 private readonly TextBox _logBox new TextBox(); public MainForm() { LocalDB.Init(); InitializeLayout(); BindEvents(); Text MES/ERP 工序BOM协同系统增强版; WindowState FormWindowState.Maximized; // 初始化日志框样式 InitLogBoxStyle(); } private void InitializeLayout() { var tab new TabControl { Dock DockStyle.Fill }; // 原有模块 tab.TabPages.Add(TaskNodeView.Create()); tab.TabPages.Add(BomView.Create()); tab.TabPages.Add(MasterSlaveView.Create()); tab.TabPages.Add(DashboardView.Create(_logBox)); // 新增模块 tab.TabPages.Add(ChartViewEx.Create(_chartEx)); // 增强版图表页 tab.TabPages.Add(DataMaintenanceView.Create()); // 数据维护页 Controls.Add(tab); } /// summary /// 初始化日志框样式 /// /summary private void InitLogBoxStyle() { _logBox.Dock DockStyle.Fill; _logBox.Multiline true; _logBox.ReadOnly true; _logBox.BackColor Color.Black; _logBox.ForeColor Color.Lime; _logBox.Font new Font(Consolas, 10); _logBox.ScrollBars ScrollBars.Vertical; } private void BindEvents() { // 任务操作事件 MesEventBus.OnTaskOperated (task, station, status) { _logBox.AppendText(${DateTime.Now:HH:mm:ss} [{station}] 任务[{task}] 状态变更为{status}\r\n); }; // 数据编辑事件 MesEventBus.OnDataEdited (table) { _logBox.AppendText(${DateTime.Now:HH:mm:ss} 数据表[{table}]发生编辑触发全系统数据刷新\r\n); // 刷新所有关联控件 TaskNodeView._dgv.DataSource TaskComponent.GetTasks(); _chartEx.Refresh(); }; // BOM计算事件 MesEventBus.OnBomCalculated (product, total) { _logBox.AppendText(${DateTime.Now:HH:mm:ss} 产品[{product}] BOM计算完成总需求{total:N2}\r\n); }; } }4. 增强版图表视图ChartViewEx.cscsharp运行using System; using System.Windows.Forms; /// summary /// 增强版图表报表视图 /// /summary public static class ChartViewEx { public static TabPage Create(GdiChartEx chart) { var page new TabPage(GDI图表报表增强版); // 顶部操作区 var topPanel new Panel { Dock DockStyle.Top, Height 80 }; var btnLoadTaskQty new Button { Text 加载工站计划产量, Dock DockStyle.Top, Width 200 }; var btnLoadBomQty new Button { Text 加载BOM物料需求, Dock DockStyle.Top, Width 200 }; var cboColor new ComboBox { Dock DockStyle.Top, Width 200, DropDownStyle ComboBoxStyle.DropDownList, DataSource new[] { 蓝色, 绿色, 橙色, 紫色 } }; topPanel.Controls.AddRange(new Control[] { btnLoadTaskQty, btnLoadBomQty, cboColor }); // 图表区 chart.Dock DockStyle.Fill; chart.BackColor Color.White; // 布局组装 page.Controls.Add(chart); page.Controls.Add(topPanel); // 事件绑定 btnLoadTaskQty.Click (s, e) { chart.DataSource LocalDB.Query( SELECT StationName, SUM(PlanQty) Qty FROM WorkTask GROUP BY StationId, StationName ORDER BY SUM(PlanQty) DESC ); chart.Refresh(); }; btnLoadBomQty.Click (s, e) { chart.DataSource LocalDB.Query( SELECT Material, SUM(UseQty*(1Loss)) TotalNeed FROM BOM WHERE ProductCodePROD001 GROUP BY Material ); chart.Refresh(); }; cboColor.SelectedIndexChanged (s, e) { chart.BarColor cboColor.Text switch { 绿色 Color.ForestGreen, 橙色 Color.Orange, 紫色 Color.Purple, _ Color.CornflowerBlue }; chart.Refresh(); }; // 初始日志 DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} 增强版图表报表初始化完成\r\n); return page; } }5. 关键补充BOM 模块增强BomView.cs 完善csharp运行using System; using System.Data; using System.Windows.Forms; public static class BomView { public static TabPage Create() { var page new TabPage(BOM物料调配增强版); // 顶部操作区 var topPanel new Panel { Dock DockStyle.Top, Height 100 }; var cboProduct new ComboBox { Dock DockStyle.Top, Width 200, DropDownStyle ComboBoxStyle.DropDownList, DataSource new[] { PROD001, PROD002 } }; var txtQty new TextBox { Dock DockStyle.Top, Width 200, PlaceholderText 输入计划产量默认1000, Text 1000 }; var btnCalc new Button { Text 计算物料需求, Dock DockStyle.Top, Width 200 }; topPanel.Controls.AddRange(new Control[] { cboProduct, txtQty, btnCalc }); // 数据展示区 var dgv new DataGridView { Dock DockStyle.Fill }; // 布局组装 page.Controls.Add(dgv); page.Controls.Add(topPanel); // 事件绑定 btnCalc.Click (s, e) { if (!int.TryParse(txtQty.Text, out int planQty) || planQty 0) { MessageBox.Show(请输入有效的计划产量正整数); return; } var product cboProduct.Text; var dt BomComponent.GetBom(product); // 添加计算列 if (!dt.Columns.Contains(TotalNeed)) dt.Columns.Add(TotalNeed, typeof(decimal)); if (!dt.Columns.Contains(LossQty)) dt.Columns.Add(LossQty, typeof(decimal)); decimal totalAll 0; foreach (DataRow r in dt.Rows) { decimal useQty decimal.Parse(r[UseQty].ToString()); decimal loss decimal.Parse(r[Loss].ToString()); // 计算净需求损耗 decimal lossQty planQty * useQty * loss; decimal total planQty * useQty lossQty; r[LossQty] lossQty; r[TotalNeed] total; totalAll total; } dgv.DataSource dt; // 格式化列 dgv.Columns[UseQty].DefaultCellStyle.Format N2; dgv.Columns[Loss].DefaultCellStyle.Format P2; dgv.Columns[LossQty].DefaultCellStyle.Format N2; dgv.Columns[TotalNeed].DefaultCellStyle.Format N2; // 触发BOM计算事件 MesEventBus.SendBom(product, totalAll); DashboardView._logBox.AppendText(${DateTime.Now:HH:mm:ss} 产品[{product}] 产量[{planQty}] BOM计算完成总物料需求{totalAll:N2}\r\n); }; return page; } }核心功能说明GDI 图表增强支持抗锯齿、渐变柱体、网格线、图例、旋转标签自适应柱宽、数值刻度自动计算支持自定义柱体颜色、字体样式数据维护模块支持 WorkStation/ProcessRoute/BOM/WorkTask 4 大核心表的增删改查批量保存修改、自动生成主键触发数据编辑事件联动刷新全系统数据BOM 物料计算增强支持多产品切换、自定义计划产量拆分净需求 / 损耗量展示格式化数值显示触发 BOM 计算事件日志记录总需求模块化架构主子结构MasterSlaveView工站 - 任务联动分节点执行端TaskNodeView任务状态变更汇总工作台DashboardView全局日志数据维护中心本地 SQLite 数据编辑GDI 图表报表多维度数据可视化部署说明将上述代码文件添加到原有 PraticDulTask 项目中确保引用System.Data.SQLite通过 NuGet 安装对应版本调整 MainForm 中 TabPage 的加载逻辑替换原有 ChartView 为 ChartViewEx运行项目后自动初始化本地 MESData.db 数据库包含 8-9 组预设测试数据支持在「数据维护中心」编辑 / 新增 / 删除基础数据所有修改实时同步到本地 SQLite 文件该实现完整覆盖了工序路径管理、BOM 物料需求计算、产线任务执行、GDI 图表可视化、本地数据维护等核心场景符合 WinForm 本地文件数据库的轻量化 MES/ERP 协同系统设计目标。
工序 BOM 协同系统架构多模块组件
以下是基于现有 MES/ERP 工序 BOM 协同系统架构完善并扩展的 WinForm 模块化实现代码涵盖GDI 图表增强、数据维护、多模块联动、本地 SQLite 数据管理等核心能力保持原有主子结构 / 分节点执行 / 汇总工作台的设计体系1. 核心扩展GDI 图表控件增强GdiChartEx.cscsharp运行using System; using System.Data; using System.Drawing; using System.Drawing.Drawing2D; using System.Windows.Forms; /// summary /// 增强版GDI柱状图控件支持多系列、图例、网格、自定义样式 /// /summary public class GdiChartEx : Panel { // 图表配置 public Color BarColor { get; set; } Color.CornflowerBlue; public Color GridColor { get; set; } Color.LightGray; public Color TextColor { get; set; } Color.Black; public Font LabelFont { get; set; } new Font(微软雅黑, 9); public Font ValueFont { get; set; } new Font(微软雅黑, 10, FontStyle.Bold); // 数据源 private DataTable _dataSource; public DataTable DataSource { get _dataSource; set { _dataSource value; Invalidate(); // 数据变更重绘 } } protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); if (DataSource null || DataSource.Rows.Count 0) return; var g e.Graphics; g.SmoothingMode SmoothingMode.AntiAlias; // 抗锯齿 g.Clear(BackColor); // 绘制网格背景 DrawGrid(g); // 计算图表区域留边距 int paddingLeft 80, paddingRight 20, paddingTop 30, paddingBottom 60; int chartWidth ClientSize.Width - paddingLeft - paddingRight; int chartHeight ClientSize.Height - paddingTop - paddingBottom; // 计算最大值用于刻度 int maxValue GetMaxValue(); float yScale (float)chartHeight / maxValue; // 绘制柱状图 int barWidth Math.Min(80, chartWidth / (DataSource.Rows.Count * 2)); // 自适应柱宽 int x paddingLeft (chartWidth - DataSource.Rows.Count * (barWidth 30)) / 2; // 居中 foreach (DataRow row in DataSource.Rows) { string category row[0].ToString(); int value int.Parse(row[1].ToString()); // 柱体坐标计算 float barHeight value * yScale; float y paddingTop (chartHeight - barHeight); // 绘制柱体渐变边框 using (var brush new LinearGradientBrush( new Point(x, (int)y), new Point(x, (int)(y barHeight)), BarColor, BarColor.Darken(30))) { g.FillRectangle(brush, x, y, barWidth, barHeight); g.DrawRectangle(Pens.Black, x, y, barWidth, barHeight); } // 绘制数值标签 var valueText value.ToString(N0); var valueSize g.MeasureString(valueText, ValueFont); g.DrawString(valueText, ValueFont, new SolidBrush(TextColor), x (barWidth - valueSize.Width) / 2, y - valueSize.Height - 5); // 绘制分类标签旋转45度避免重叠 g.TranslateTransform(x barWidth / 2, ClientSize.Height - paddingBottom 20); g.RotateTransform(-45); g.DrawString(category, LabelFont, new SolidBrush(Color.DarkRed), -g.MeasureString(category, LabelFont).Width / 2, 0); g.ResetTransform(); x barWidth 30; // 柱间距 } // 绘制图例 DrawLegend(g); // 日志记录 DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} 增强版柱状图绘制完成 | 数据行数{DataSource.Rows.Count}\r\n); } /// summary /// 绘制网格线 /// /summary private void DrawGrid(Graphics g) { int paddingLeft 80, paddingTop 30, paddingBottom 60; int chartHeight ClientSize.Height - paddingTop - paddingBottom; int chartWidth ClientSize.Width - paddingLeft - 20; // 横向网格5等分 int gridCount 5; float gridStep chartHeight / gridCount; for (int i 0; i gridCount; i) { float y paddingTop i * gridStep; using (var pen new Pen(GridColor, 1)) { g.DrawLine(pen, paddingLeft, y, ClientSize.Width - 20, y); } // 刻度值 int scaleValue (int)((chartHeight - i * gridStep) / chartHeight * GetMaxValue()); g.DrawString(scaleValue.ToString(), LabelFont, Brushes.Gray, paddingLeft - 50, y - 10); } // 纵向基线 g.DrawLine(Pens.Black, paddingLeft, paddingTop, paddingLeft, ClientSize.Height - paddingBottom); g.DrawLine(Pens.Black, paddingLeft, ClientSize.Height - paddingBottom, ClientSize.Width - 20, ClientSize.Height - paddingBottom); } /// summary /// 绘制图例 /// /summary private void DrawLegend(Graphics g) { int legendX ClientSize.Width - 150; int legendY 20; // 图例框 g.FillRectangle(Brushes.White, legendX, legendY, 120, 40); g.DrawRectangle(Pens.Black, legendX, legendY, 120, 40); // 图例颜色块 g.FillRectangle(new SolidBrush(BarColor), legendX 10, legendY 10, 20, 15); g.DrawRectangle(Pens.Black, legendX 10, legendY 10, 20, 15); // 图例文字 g.DrawString(计划产量, LabelFont, Brushes.Black, legendX 35, legendY 10); } /// summary /// 获取数据源最大值用于刻度计算 /// /summary private int GetMaxValue() { int max 0; foreach (DataRow row in DataSource.Rows) { int val int.Parse(row[1].ToString()); if (val max) max val; } // 向上取整到最近的10的倍数 return (int)Math.Ceiling(max / 10.0) * 10; } /// summary /// 刷新图表 /// /summary public new void Refresh() Invalidate(); } /// summary /// Color扩展方法 /// /summary public static class ColorExtensions { public static Color Darken(this Color color, int percent) { float factor 1 - percent / 100f; return Color.FromArgb( color.A, (int)(color.R * factor), (int)(color.G * factor), (int)(color.B * factor)); } }2. 数据维护模块DataMaintenanceView.cscsharp运行using System; using System.Data; using System.Windows.Forms; /// summary /// 本地数据库数据维护模块支持8-9组基础数据编辑/新增/删除 /// /summary public static class DataMaintenanceView { // 支持维护的表名 private static readonly string[] _maintainTables { WorkStation, ProcessRoute, BOM, WorkTask }; public static TabPage Create() { var page new TabPage(数据维护中心); // 顶部选择区 var topPanel new Panel { Dock DockStyle.Top, Height 60 }; var cboTable new ComboBox { Dock DockStyle.Left, Width 200, DataSource _maintainTables, DropDownStyle ComboBoxStyle.DropDownList }; var btnRefresh new Button { Text 刷新数据, Dock DockStyle.Left, Width 100 }; var btnSave new Button { Text 保存修改, Dock DockStyle.Left, Width 100 }; var btnAdd new Button { Text 新增行, Dock DockStyle.Left, Width 100 }; var btnDel new Button { Text 删除选中, Dock DockStyle.Left, Width 100 }; topPanel.Controls.AddRange(new Control[] { cboTable, btnRefresh, btnSave, btnAdd, btnDel }); // 数据展示区 var dgvData new DataGridView { Dock DockStyle.Fill, AllowUserToAddRows false, AllowUserToDeleteRows false, AutoGenerateColumns true, ReadOnly false }; // 布局组装 page.Controls.Add(dgvData); page.Controls.Add(topPanel); // 事件绑定 cboTable.SelectedIndexChanged (s, e) LoadTableData(cboTable.Text, dgvData); btnRefresh.Click (s, e) LoadTableData(cboTable.Text, dgvData); btnAdd.Click (s, e) AddNewRow(cboTable.Text, dgvData); btnDel.Click (s, e) DeleteSelectedRow(cboTable.Text, dgvData); btnSave.Click (s, e) SaveTableChanges(cboTable.Text, dgvData); // 初始加载第一个表 if (_maintainTables.Length 0) LoadTableData(_maintainTables[0], dgvData); return page; } /// summary /// 加载指定表数据 /// /summary private static void LoadTableData(string tableName, DataGridView dgv) { try { var dt LocalDB.Query($SELECT * FROM {tableName}); dgv.DataSource dt; DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} 加载[{tableName}]数据完成共{dt.Rows.Count}行\r\n); } catch (Exception ex) { MessageBox.Show($加载数据失败{ex.Message}); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} 加载[{tableName}]数据失败{ex.Message}\r\n); } } /// summary /// 新增行 /// /summary private static void AddNewRow(string tableName, DataGridView dgv) { if (dgv.DataSource is not DataTable dt) return; var newRow dt.NewRow(); // 自动填充主键简单自增逻辑 var maxId LocalDB.Query($SELECT MAX(Id) FROM {tableName}).Rows[0][0]; newRow[Id] maxId DBNull.Value ? 1 : Convert.ToInt32(maxId) 1; dt.Rows.Add(newRow); dgv.Refresh(); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]新增空白行\r\n); } /// summary /// 删除选中行 /// /summary private static void DeleteSelectedRow(string tableName, DataGridView dgv) { if (dgv.CurrentRow null) { MessageBox.Show(请选中要删除的行); return; } var id dgv.CurrentRow.Cells[Id].Value; if (id DBNull.Value || id null) { MessageBox.Show(无效的行ID); return; } try { LocalDB.Execute($DELETE FROM {tableName} WHERE Id{id}); LoadTableData(tableName, dgv); // 刷新数据 DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]删除ID{id}的行\r\n); MesEventBus.SendEdit(tableName); // 触发数据编辑事件 } catch (Exception ex) { MessageBox.Show($删除失败{ex.Message}); } } /// summary /// 保存修改批量更新 /// /summary private static void SaveTableChanges(string tableName, DataGridView dgv) { if (dgv.DataSource is not DataTable dt) return; try { foreach (DataRow row in dt.Rows) { if (row.RowState DataRowState.Modified) { // 构建更新SQL通用逻辑适配4个核心表 string sql BuildUpdateSql(tableName, row); LocalDB.Execute(sql); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]更新ID{row[Id]}{sql}\r\n); } else if (row.RowState DataRowState.Added) { // 构建插入SQL string sql BuildInsertSql(tableName, row); LocalDB.Execute(sql); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]新增ID{row[Id]}{sql}\r\n); } } // 提交后刷新 dt.AcceptChanges(); LoadTableData(tableName, dgv); MesEventBus.SendEdit(tableName); // 触发数据编辑事件 MessageBox.Show(数据保存成功); } catch (Exception ex) { MessageBox.Show($保存失败{ex.Message}); DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} [{tableName}]保存失败{ex.Message}\r\n); } } /// summary /// 构建更新SQL /// /summary private static string BuildUpdateSql(string tableName, DataRow row) { return tableName switch { WorkStation $UPDATE WorkStation SET Name{row[Name]}, Role{row[Role]} WHERE Id{row[Id]}, ProcessRoute $UPDATE ProcessRoute SET Name{row[Name]}, StationId{row[StationId]}, Sort{row[Sort]} WHERE Id{row[Id]}, BOM $UPDATE BOM SET ProductCode{row[ProductCode]}, Material{row[Material]}, UseQty{row[UseQty]}, Loss{row[Loss]} WHERE Id{row[Id]}, WorkTask $UPDATE WorkTask SET TaskNo{row[TaskNo]}, StationId{row[StationId]}, StationName{row[StationName]}, Status{row[Status]}, PlanQty{row[PlanQty]} WHERE Id{row[Id]}, _ throw new NotSupportedException($不支持的表{tableName}) }; } /// summary /// 构建插入SQL /// /summary private static string BuildInsertSql(string tableName, DataRow row) { return tableName switch { WorkStation $INSERT INTO WorkStation VALUES({row[Id]},{row[Name]},{row[Role]}), ProcessRoute $INSERT INTO ProcessRoute VALUES({row[Id]},{row[Name]},{row[StationId]},{row[Sort]}), BOM $INSERT INTO BOM VALUES({row[Id]},{row[ProductCode]},{row[Material]},{row[UseQty]},{row[Loss]}), WorkTask $INSERT INTO WorkTask VALUES({row[Id]},{row[TaskNo]},{row[StationId]},{row[StationName]},{row[Status]},{row[PlanQty]}), _ throw new NotSupportedException($不支持的表{tableName}) }; } }3. 主窗体扩展MainForm.cs 完善csharp运行using System; using System.Data; using System.Drawing; using System.Windows.Forms; public partial class MainForm : Form { private readonly GdiChartEx _chartEx new GdiChartEx(); // 替换为增强版图表 private readonly TextBox _logBox new TextBox(); public MainForm() { LocalDB.Init(); InitializeLayout(); BindEvents(); Text MES/ERP 工序BOM协同系统增强版; WindowState FormWindowState.Maximized; // 初始化日志框样式 InitLogBoxStyle(); } private void InitializeLayout() { var tab new TabControl { Dock DockStyle.Fill }; // 原有模块 tab.TabPages.Add(TaskNodeView.Create()); tab.TabPages.Add(BomView.Create()); tab.TabPages.Add(MasterSlaveView.Create()); tab.TabPages.Add(DashboardView.Create(_logBox)); // 新增模块 tab.TabPages.Add(ChartViewEx.Create(_chartEx)); // 增强版图表页 tab.TabPages.Add(DataMaintenanceView.Create()); // 数据维护页 Controls.Add(tab); } /// summary /// 初始化日志框样式 /// /summary private void InitLogBoxStyle() { _logBox.Dock DockStyle.Fill; _logBox.Multiline true; _logBox.ReadOnly true; _logBox.BackColor Color.Black; _logBox.ForeColor Color.Lime; _logBox.Font new Font(Consolas, 10); _logBox.ScrollBars ScrollBars.Vertical; } private void BindEvents() { // 任务操作事件 MesEventBus.OnTaskOperated (task, station, status) { _logBox.AppendText(${DateTime.Now:HH:mm:ss} [{station}] 任务[{task}] 状态变更为{status}\r\n); }; // 数据编辑事件 MesEventBus.OnDataEdited (table) { _logBox.AppendText(${DateTime.Now:HH:mm:ss} 数据表[{table}]发生编辑触发全系统数据刷新\r\n); // 刷新所有关联控件 TaskNodeView._dgv.DataSource TaskComponent.GetTasks(); _chartEx.Refresh(); }; // BOM计算事件 MesEventBus.OnBomCalculated (product, total) { _logBox.AppendText(${DateTime.Now:HH:mm:ss} 产品[{product}] BOM计算完成总需求{total:N2}\r\n); }; } }4. 增强版图表视图ChartViewEx.cscsharp运行using System; using System.Windows.Forms; /// summary /// 增强版图表报表视图 /// /summary public static class ChartViewEx { public static TabPage Create(GdiChartEx chart) { var page new TabPage(GDI图表报表增强版); // 顶部操作区 var topPanel new Panel { Dock DockStyle.Top, Height 80 }; var btnLoadTaskQty new Button { Text 加载工站计划产量, Dock DockStyle.Top, Width 200 }; var btnLoadBomQty new Button { Text 加载BOM物料需求, Dock DockStyle.Top, Width 200 }; var cboColor new ComboBox { Dock DockStyle.Top, Width 200, DropDownStyle ComboBoxStyle.DropDownList, DataSource new[] { 蓝色, 绿色, 橙色, 紫色 } }; topPanel.Controls.AddRange(new Control[] { btnLoadTaskQty, btnLoadBomQty, cboColor }); // 图表区 chart.Dock DockStyle.Fill; chart.BackColor Color.White; // 布局组装 page.Controls.Add(chart); page.Controls.Add(topPanel); // 事件绑定 btnLoadTaskQty.Click (s, e) { chart.DataSource LocalDB.Query( SELECT StationName, SUM(PlanQty) Qty FROM WorkTask GROUP BY StationId, StationName ORDER BY SUM(PlanQty) DESC ); chart.Refresh(); }; btnLoadBomQty.Click (s, e) { chart.DataSource LocalDB.Query( SELECT Material, SUM(UseQty*(1Loss)) TotalNeed FROM BOM WHERE ProductCodePROD001 GROUP BY Material ); chart.Refresh(); }; cboColor.SelectedIndexChanged (s, e) { chart.BarColor cboColor.Text switch { 绿色 Color.ForestGreen, 橙色 Color.Orange, 紫色 Color.Purple, _ Color.CornflowerBlue }; chart.Refresh(); }; // 初始日志 DashboardView._logBox?.AppendText(${DateTime.Now:HH:mm:ss} 增强版图表报表初始化完成\r\n); return page; } }5. 关键补充BOM 模块增强BomView.cs 完善csharp运行using System; using System.Data; using System.Windows.Forms; public static class BomView { public static TabPage Create() { var page new TabPage(BOM物料调配增强版); // 顶部操作区 var topPanel new Panel { Dock DockStyle.Top, Height 100 }; var cboProduct new ComboBox { Dock DockStyle.Top, Width 200, DropDownStyle ComboBoxStyle.DropDownList, DataSource new[] { PROD001, PROD002 } }; var txtQty new TextBox { Dock DockStyle.Top, Width 200, PlaceholderText 输入计划产量默认1000, Text 1000 }; var btnCalc new Button { Text 计算物料需求, Dock DockStyle.Top, Width 200 }; topPanel.Controls.AddRange(new Control[] { cboProduct, txtQty, btnCalc }); // 数据展示区 var dgv new DataGridView { Dock DockStyle.Fill }; // 布局组装 page.Controls.Add(dgv); page.Controls.Add(topPanel); // 事件绑定 btnCalc.Click (s, e) { if (!int.TryParse(txtQty.Text, out int planQty) || planQty 0) { MessageBox.Show(请输入有效的计划产量正整数); return; } var product cboProduct.Text; var dt BomComponent.GetBom(product); // 添加计算列 if (!dt.Columns.Contains(TotalNeed)) dt.Columns.Add(TotalNeed, typeof(decimal)); if (!dt.Columns.Contains(LossQty)) dt.Columns.Add(LossQty, typeof(decimal)); decimal totalAll 0; foreach (DataRow r in dt.Rows) { decimal useQty decimal.Parse(r[UseQty].ToString()); decimal loss decimal.Parse(r[Loss].ToString()); // 计算净需求损耗 decimal lossQty planQty * useQty * loss; decimal total planQty * useQty lossQty; r[LossQty] lossQty; r[TotalNeed] total; totalAll total; } dgv.DataSource dt; // 格式化列 dgv.Columns[UseQty].DefaultCellStyle.Format N2; dgv.Columns[Loss].DefaultCellStyle.Format P2; dgv.Columns[LossQty].DefaultCellStyle.Format N2; dgv.Columns[TotalNeed].DefaultCellStyle.Format N2; // 触发BOM计算事件 MesEventBus.SendBom(product, totalAll); DashboardView._logBox.AppendText(${DateTime.Now:HH:mm:ss} 产品[{product}] 产量[{planQty}] BOM计算完成总物料需求{totalAll:N2}\r\n); }; return page; } }核心功能说明GDI 图表增强支持抗锯齿、渐变柱体、网格线、图例、旋转标签自适应柱宽、数值刻度自动计算支持自定义柱体颜色、字体样式数据维护模块支持 WorkStation/ProcessRoute/BOM/WorkTask 4 大核心表的增删改查批量保存修改、自动生成主键触发数据编辑事件联动刷新全系统数据BOM 物料计算增强支持多产品切换、自定义计划产量拆分净需求 / 损耗量展示格式化数值显示触发 BOM 计算事件日志记录总需求模块化架构主子结构MasterSlaveView工站 - 任务联动分节点执行端TaskNodeView任务状态变更汇总工作台DashboardView全局日志数据维护中心本地 SQLite 数据编辑GDI 图表报表多维度数据可视化部署说明将上述代码文件添加到原有 PraticDulTask 项目中确保引用System.Data.SQLite通过 NuGet 安装对应版本调整 MainForm 中 TabPage 的加载逻辑替换原有 ChartView 为 ChartViewEx运行项目后自动初始化本地 MESData.db 数据库包含 8-9 组预设测试数据支持在「数据维护中心」编辑 / 新增 / 删除基础数据所有修改实时同步到本地 SQLite 文件该实现完整覆盖了工序路径管理、BOM 物料需求计算、产线任务执行、GDI 图表可视化、本地数据维护等核心场景符合 WinForm 本地文件数据库的轻量化 MES/ERP 协同系统设计目标。