using System; using System.Collections.Generic; using System.Data; using System.Globalization; using System.IO; using System.Windows.Forms; using System.Data.SQLite; using Smart.CustomComponents; namespace 成套报价软件 { /// /// 调系数 的交互逻辑(事件处理)。 /// UI 布局定义见 调系数.ui.cs。 /// 本窗体作为 MainForm 的对话框使用,故不含独立的 Main 入口点。 /// /// 功能:批量修改元件表中的"采购系数"和"报出系数"。 /// - multiSelectComboBox1:多选元件类型(元件/壳体/母排),只显示/修改所选类型 /// - resizableComboBox1:调整范围(项目/图号) /// - resizableComboBox2:图号列表(范围=图号时启用) /// - resizableComboBox3:选择要修改的系数(采购系数/报价系数) /// - resizableComboBox4:修改方法(等于/加/减) /// - resizableTextBox1:输入数值 /// - button1:启动调整 /// public partial class 调系数 { // ====== 常量 ====== /// 图号表名(与主窗口一致)。 private const string TuhaoTableName = "advancedDataGridView9Table"; // ====== 字段 ====== /// 当前项目编号(主窗口传入)。 private string _projectId; /// 当前项目的报价库绝对路径。 private string _quoteDbPath; /// 内存中的全部元件行(已按当前类型筛选)。 private readonly List _allRows = new List(); // ====== 内部数据结构 ====== /// 元件表一行的内存表示(含定位信息,用于回写数据库)。 private class YuanjianRow { public string Tuhao; // 所属图号 public string Xianghao; // 所属箱号 public string RealTableName; // 该箱号元件表的真实表名(SQLite 表名) public long RowId; // 在元件表中的 rowid public string Name; // 元件名称 public string Unit; // 单位 public string Count; // 数量 public string CaiGouXishu; // 原采购系数 public string BaoChuXishu; // 原报出系数 public string Leixing; // 类型 } // ====== 构造函数 ====== /// 无参构造函数(设计器/独立测试用,不绑定业务逻辑)。 public 调系数() { InitializeComponent(); } /// /// 带项目编号的构造函数(主窗口调用此版本)。 /// 传入后窗体在 Load 时会从该项目的报价库加载数据。 /// /// 当前项目编号。 public 调系数(string projectId) { InitializeComponent(); _projectId = projectId; _quoteDbPath = GetQuoteDbPath(projectId); this.Load += 调系数_Load; this.multiSelectComboBox1.SelectedItemsChanged += multiSelectComboBox1_SelectedIndexChanged; this.resizableComboBox1.SelectedIndexChanged += resizableComboBox1_SelectedIndexChanged; this.resizableComboBox2.SelectedIndexChanged += resizableComboBox2_SelectedIndexChanged; this.resizableComboBox3.SelectedIndexChanged += OnPreviewChanged; this.resizableComboBox4.SelectedIndexChanged += OnPreviewChanged; this.resizableTextBox1.TextChanged += OnPreviewChanged; this.button1.Click += button1_Click; } // ====== 窗体事件 ====== private void 调系数_Load(object sender, EventArgs e) { // 默认勾选"元件"(用户最常用) if (this.multiSelectComboBox1.Items.Count > 0) // multiSelectComboBox 默认不选中 // 默认范围=项目,resizableComboBox2 禁用 this.resizableComboBox2.Enabled = false; // 表格只读(只用于预览,不能直接编辑) this.advancedDataGridView1.ReadOnly = true; this.advancedDataGridView1.AllowUserToAddRows = false; this.advancedDataGridView1.AllowUserToDeleteRows = false; this.advancedDataGridView1.SelectionMode = DataGridViewSelectionMode.CellSelect; // 加载数据 ReloadData(); } /// multiSelectComboBox1 选择变化:重新加载表格数据(按所选类型筛选)。 private void multiSelectComboBox1_SelectedIndexChanged(object sender, EventArgs e) { // SelectedItemsChanged 在多选场景下会触发多次,用 BeginInvoke 合并到 UI 空闲时执行 this.BeginInvoke((Action)ReloadData); } /// resizableComboBox1 选择变化:切换"项目/图号"模式。 private void resizableComboBox1_SelectedIndexChanged(object sender, EventArgs e) { string scope = this.resizableComboBox1.Text; if (scope == "图号") { this.resizableComboBox2.Enabled = true; LoadTuhaoList(); } else { this.resizableComboBox2.Enabled = false; this.resizableComboBox2.Items.Clear(); ReloadData(); } } /// resizableComboBox2 选择变化:加载指定图号的数据。 private void resizableComboBox2_SelectedIndexChanged(object sender, EventArgs e) { if (!this.resizableComboBox2.Enabled) return; ReloadData(); } /// 修改规则变化(系数类型/方法/数值):只刷新"新系数"列预览,不写库。 private void OnPreviewChanged(object sender, EventArgs e) { RefreshPreviewColumns(); } // ====== button1:启动调整 ====== private void button1_Click(object sender, EventArgs e) { if (_allRows.Count == 0) { MessageBox.Show(this, "没有可调整的数据,请先选择类型和范围。", "调系数", MessageBoxButtons.OK, MessageBoxIcon.Information); return; } // 解析输入数值 string numStr = this.resizableTextBox1.Text.Trim(); double numVal; if (!double.TryParse(numStr, NumberStyles.Any, CultureInfo.InvariantCulture, out numVal) && !double.TryParse(numStr, NumberStyles.Any, CultureInfo.CurrentCulture, out numVal)) { MessageBox.Show(this, "请输入有效的数字。", "调系数", MessageBoxButtons.OK, MessageBoxIcon.Warning); this.resizableTextBox1.Focus(); return; } string xishu = this.resizableComboBox3.Text; // 采购系数 / 报价系数 string method = this.resizableComboBox4.Text; // 等于 / 加 / 减 string colName = (xishu == "采购系数") ? "采购系数" : "报出系数"; // 二次确认 string scope = this.resizableComboBox1.Text; string tuhaoInfo = (scope == "图号") ? "图号[" + this.resizableComboBox2.Text + "]" : "整个项目"; string typeInfo = string.Join("/", GetSelectedTypes()); if (typeInfo == "") typeInfo = "(未选类型)"; string msg = string.Format( "将调整 {0} 内所有类型为 [{1}] 的元件行\n" + "调整对象:{2}\n调整方法:{3} {4}\n\n确认继续?", tuhaoInfo, typeInfo, colName, method, numStr); if (MessageBox.Show(this, msg, "调系数确认", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) != DialogResult.OK) return; // 写回数据库 int affected = 0; try { if (!File.Exists(_quoteDbPath)) { MessageBox.Show(this, "报价库不存在:" + _quoteDbPath, "调系数", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } using (var conn = new SQLiteConnection(GetConnString(_quoteDbPath))) { conn.Open(); using (var tx = conn.BeginTransaction()) { // 按 RealTableName 分组,减少重复 SQL 解析 var groups = new Dictionary>(); foreach (var r in _allRows) { if (string.IsNullOrEmpty(r.RealTableName)) continue; if (!groups.ContainsKey(r.RealTableName)) groups[r.RealTableName] = new List(); groups[r.RealTableName].Add(r); } foreach (var kv in groups) { string tbl = "\"" + kv.Key + "\""; string sql = "UPDATE " + tbl + " SET " + colName + " = @v WHERE rowid = @rid;"; using (var cmd = new SQLiteCommand(sql, conn, tx)) { cmd.Parameters.Add("@v", DbType.String); cmd.Parameters.Add("@rid", DbType.Int64); foreach (var r in kv.Value) { double oldVal = ParseXishu(r.CaiGouXishu, r.BaoChuXishu, colName); double newVal = ApplyMethod(oldVal, method, numVal); cmd.Parameters["@v"].Value = newVal.ToString("0.######", CultureInfo.InvariantCulture); cmd.Parameters["@rid"].Value = r.RowId; affected += cmd.ExecuteNonQuery(); } } } tx.Commit(); } } MessageBox.Show(this, "调整完成,共更新 " + affected + " 条记录。", "调系数", MessageBoxButtons.OK, MessageBoxIcon.Information); } catch (Exception ex) { MessageBox.Show(this, "调整失败:" + ex.Message, "调系数", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } // 重新加载数据(刷新原系数列) ReloadData(); } // ====== 数据加载 ====== /// 根据当前选择重新加载表格数据。 private void ReloadData() { _allRows.Clear(); this.advancedDataGridView1.Rows.Clear(); if (string.IsNullOrEmpty(_projectId) || !File.Exists(_quoteDbPath)) return; var selTypes = GetSelectedTypes(); if (selTypes.Count == 0) return; string scope = this.resizableComboBox1.Text; string targetTuhao = null; if (scope == "图号") { targetTuhao = this.resizableComboBox2.Text; if (string.IsNullOrEmpty(targetTuhao)) return; } try { using (var conn = new SQLiteConnection(GetConnString(_quoteDbPath))) { conn.Open(); // 取所有图号名 var tuhaoList = new List(); if (targetTuhao != null) { tuhaoList.Add(targetTuhao); } else { string sqlTh = "SELECT 图号 FROM " + TuhaoTableName + " ORDER BY rowid;"; using (var cmd = new SQLiteCommand(sqlTh, conn)) using (var rdr = cmd.ExecuteReader()) { while (rdr.Read()) { string t = rdr.IsDBNull(0) ? "" : rdr.GetValue(0).ToString(); if (!string.IsNullOrEmpty(t)) tuhaoList.Add(t); } } } foreach (var th in tuhaoList) { // 取该图号的箱号表真实表名 string xhTbl = LookupTableByIndex(conn, "箱号", th, ""); if (string.IsNullOrEmpty(xhTbl)) continue; // 取该图号的所有箱号 var xhList = new List(); try { string sqlXh = "SELECT 箱号 FROM \"" + xhTbl + "\" ORDER BY rowid;"; using (var cmd = new SQLiteCommand(sqlXh, conn)) using (var rdr = cmd.ExecuteReader()) { while (rdr.Read()) { string x = rdr.IsDBNull(0) ? "" : rdr.GetValue(0).ToString(); if (!string.IsNullOrEmpty(x)) xhList.Add(x); } } } catch { continue; } foreach (var xh in xhList) { string yjTbl = LookupTableByIndex(conn, "元件", th, xh); if (string.IsNullOrEmpty(yjTbl)) continue; try { string sqlYj = "SELECT rowid, 元件名称, 单位, 数量, 采购系数, 报出系数, 类型 " + "FROM \"" + yjTbl + "\" ORDER BY rowid;"; using (var cmd = new SQLiteCommand(sqlYj, conn)) using (var rdr = cmd.ExecuteReader()) { while (rdr.Read()) { string leixing = rdr.IsDBNull(6) ? "" : rdr.GetValue(6).ToString(); // 只保留所选类型 if (!selTypes.Contains(leixing)) continue; var r = new YuanjianRow(); r.Tuhao = th; r.Xianghao = xh; r.RealTableName = yjTbl; r.RowId = rdr.GetInt64(0); r.Name = rdr.IsDBNull(1) ? "" : rdr.GetValue(1).ToString(); r.Unit = rdr.IsDBNull(2) ? "" : rdr.GetValue(2).ToString(); r.Count = rdr.IsDBNull(3) ? "" : rdr.GetValue(3).ToString(); r.CaiGouXishu = rdr.IsDBNull(4) ? "" : rdr.GetValue(4).ToString(); r.BaoChuXishu = rdr.IsDBNull(5) ? "" : rdr.GetValue(5).ToString(); r.Leixing = leixing; _allRows.Add(r); } } } catch { } } } } // 填充表格(按图号→箱号顺序,顺序与 _allRows 一致) this.advancedDataGridView1.RowCount = 0; foreach (var r in _allRows) { int idx = this.advancedDataGridView1.Rows.Add(); var row = this.advancedDataGridView1.Rows[idx]; row.Cells[0].Value = r.Name; row.Cells[1].Value = r.Unit; row.Cells[2].Value = r.Count; row.Cells[3].Value = r.CaiGouXishu; row.Cells[4].Value = ""; // 新采购系数(预览) row.Cells[5].Value = r.BaoChuXishu; row.Cells[6].Value = ""; // 新报价系数(预览) row.Tag = r; } // 计算预览列 RefreshPreviewColumns(); } catch (Exception ex) { MessageBox.Show(this, "加载数据失败:" + ex.Message, "调系数", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// 刷新"新采购系数/新报价系数"两列的预览值(根据当前规则不写库)。 private void RefreshPreviewColumns() { string xishu = this.resizableComboBox3.Text; string method = this.resizableComboBox4.Text; string numStr = this.resizableTextBox1.Text.Trim(); double numVal; bool okNum = double.TryParse(numStr, NumberStyles.Any, CultureInfo.InvariantCulture, out numVal) || double.TryParse(numStr, NumberStyles.Any, CultureInfo.CurrentCulture, out numVal); bool modifyCaiGou = (xishu == "采购系数"); bool modifyBaoChu = (xishu == "报价系数"); for (int i = 0; i < this.advancedDataGridView1.Rows.Count; i++) { var row = this.advancedDataGridView1.Rows[i]; var r = row.Tag as YuanjianRow; if (r == null) continue; // 新采购系数列 if (modifyCaiGou && okNum) { double oldVal = ParseXishu(r.CaiGouXishu, r.BaoChuXishu, "采购系数"); row.Cells[4].Value = ApplyMethod(oldVal, method, numVal) .ToString("0.######", CultureInfo.InvariantCulture); } else { row.Cells[4].Value = r.CaiGouXishu; } // 新报价系数列 if (modifyBaoChu && okNum) { double oldVal = ParseXishu(r.CaiGouXishu, r.BaoChuXishu, "报出系数"); row.Cells[6].Value = ApplyMethod(oldVal, method, numVal) .ToString("0.######", CultureInfo.InvariantCulture); } else { row.Cells[6].Value = r.BaoChuXishu; } } } /// 把所有图号加载到 resizableComboBox2。 private void LoadTuhaoList() { this.resizableComboBox2.Items.Clear(); if (string.IsNullOrEmpty(_projectId) || !File.Exists(_quoteDbPath)) return; try { using (var conn = new SQLiteConnection(GetConnString(_quoteDbPath))) { conn.Open(); string sql = "SELECT 图号 FROM " + TuhaoTableName + " ORDER BY rowid;"; using (var cmd = new SQLiteCommand(sql, conn)) using (var rdr = cmd.ExecuteReader()) { while (rdr.Read()) { string t = rdr.IsDBNull(0) ? "" : rdr.GetValue(0).ToString(); if (!string.IsNullOrEmpty(t)) this.resizableComboBox2.Items.Add(t); } } } if (this.resizableComboBox2.Items.Count > 0) this.resizableComboBox2.SelectedIndex = 0; else ReloadData(); } catch { } } // ====== 辅助方法 ====== /// 获取 multiSelectComboBox1 选中的类型列表(元件/壳体/母排)。 private List GetSelectedTypes() { var result = new List(); var valsList = this.multiSelectComboBox1.SelectedValues; if (valsList != null) foreach (var v in valsList) if (!string.IsNullOrEmpty(v)) result.Add(v); return result; } /// 根据系数列名从行中解析原系数值(空值当 1.0)。 private double ParseXishu(string caiGou, string baoChu, string colName) { string s = (colName == "采购系数") ? caiGou : baoChu; double d; if (double.TryParse(s, NumberStyles.Any, CultureInfo.InvariantCulture, out d)) return d; if (double.TryParse(s, NumberStyles.Any, CultureInfo.CurrentCulture, out d)) return d; return 1.0; // 默认值 } /// 按"等于/加/减"方法应用数值。 private double ApplyMethod(double oldVal, string method, double numVal) { switch (method) { case "加": return oldVal + numVal; case "减": return oldVal - numVal; case "等于": default: return numVal; } } private string GetQuoteDbPath(string projectId) { string safeName = projectId; foreach (char c in Path.GetInvalidFileNameChars()) safeName = safeName.Replace(c, '_'); return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"sjk\bjb\项目" + safeName + ".db"); } private string GetConnString(string dbPath) { return "Data Source=" + dbPath + ";Version=3;"; } /// 查表索引(复用连接版本)。 private string LookupTableByIndex(SQLiteConnection conn, string type, string tuhao, string xianghao) { if (conn == null || string.IsNullOrEmpty(tuhao)) return ""; try { string sql = "SELECT 实际表名 FROM 表索引 WHERE 类型=@t AND 图号=@th AND 箱号=@xh LIMIT 1;"; using (var cmd = new SQLiteCommand(sql, conn)) { cmd.Parameters.AddWithValue("@t", type); cmd.Parameters.AddWithValue("@th", tuhao); cmd.Parameters.AddWithValue("@xh", xianghao ?? ""); object o = cmd.ExecuteScalar(); return (o == null || o == DBNull.Value) ? "" : o.ToString(); } } catch { return ""; } } } }