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)
{
// ★ A2(2026-10-01):原为裸串(无 WAL/超时,并发易锁死)——统一收敛到 数据库连接.串
return 数据库连接.串(dbPath);
}
/// 查表索引(复用连接版本)。
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 ""; }
}
}
}