530 lines
23 KiB
C#
530 lines
23 KiB
C#
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 成套报价软件
|
|
{
|
|
/// <summary>
|
|
/// 调系数 的交互逻辑(事件处理)。
|
|
/// UI 布局定义见 调系数.ui.cs。
|
|
/// 本窗体作为 MainForm 的对话框使用,故不含独立的 Main 入口点。
|
|
///
|
|
/// 功能:批量修改元件表中的"采购系数"和"报出系数"。
|
|
/// - multiSelectComboBox1:多选元件类型(元件/壳体/母排),只显示/修改所选类型
|
|
/// - resizableComboBox1:调整范围(项目/图号)
|
|
/// - resizableComboBox2:图号列表(范围=图号时启用)
|
|
/// - resizableComboBox3:选择要修改的系数(采购系数/报价系数)
|
|
/// - resizableComboBox4:修改方法(等于/加/减)
|
|
/// - resizableTextBox1:输入数值
|
|
/// - button1:启动调整
|
|
/// </summary>
|
|
public partial class 调系数
|
|
{
|
|
// ====== 常量 ======
|
|
|
|
/// <summary>图号表名(与主窗口一致)。</summary>
|
|
private const string TuhaoTableName = "advancedDataGridView9Table";
|
|
|
|
// ====== 字段 ======
|
|
|
|
/// <summary>当前项目编号(主窗口传入)。</summary>
|
|
private string _projectId;
|
|
|
|
/// <summary>当前项目的报价库绝对路径。</summary>
|
|
private string _quoteDbPath;
|
|
|
|
/// <summary>内存中的全部元件行(已按当前类型筛选)。</summary>
|
|
private readonly List<YuanjianRow> _allRows = new List<YuanjianRow>();
|
|
|
|
// ====== 内部数据结构 ======
|
|
|
|
/// <summary>元件表一行的内存表示(含定位信息,用于回写数据库)。</summary>
|
|
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; // 类型
|
|
}
|
|
|
|
// ====== 构造函数 ======
|
|
|
|
/// <summary>无参构造函数(设计器/独立测试用,不绑定业务逻辑)。</summary>
|
|
public 调系数()
|
|
{
|
|
InitializeComponent();
|
|
}
|
|
|
|
/// <summary>
|
|
/// 带项目编号的构造函数(主窗口调用此版本)。
|
|
/// 传入后窗体在 Load 时会从该项目的报价库加载数据。
|
|
/// </summary>
|
|
/// <param name="projectId">当前项目编号。</param>
|
|
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();
|
|
}
|
|
|
|
/// <summary>multiSelectComboBox1 选择变化:重新加载表格数据(按所选类型筛选)。</summary>
|
|
private void multiSelectComboBox1_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
// SelectedItemsChanged 在多选场景下会触发多次,用 BeginInvoke 合并到 UI 空闲时执行
|
|
this.BeginInvoke((Action)ReloadData);
|
|
}
|
|
|
|
/// <summary>resizableComboBox1 选择变化:切换"项目/图号"模式。</summary>
|
|
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();
|
|
}
|
|
}
|
|
|
|
/// <summary>resizableComboBox2 选择变化:加载指定图号的数据。</summary>
|
|
private void resizableComboBox2_SelectedIndexChanged(object sender, EventArgs e)
|
|
{
|
|
if (!this.resizableComboBox2.Enabled) return;
|
|
ReloadData();
|
|
}
|
|
|
|
/// <summary>修改规则变化(系数类型/方法/数值):只刷新"新系数"列预览,不写库。</summary>
|
|
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<string, List<YuanjianRow>>();
|
|
foreach (var r in _allRows)
|
|
{
|
|
if (string.IsNullOrEmpty(r.RealTableName)) continue;
|
|
if (!groups.ContainsKey(r.RealTableName))
|
|
groups[r.RealTableName] = new List<YuanjianRow>();
|
|
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();
|
|
}
|
|
|
|
// ====== 数据加载 ======
|
|
|
|
/// <summary>根据当前选择重新加载表格数据。</summary>
|
|
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<string>();
|
|
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<string>();
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>刷新"新采购系数/新报价系数"两列的预览值(根据当前规则不写库)。</summary>
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>把所有图号加载到 resizableComboBox2。</summary>
|
|
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 { }
|
|
}
|
|
|
|
// ====== 辅助方法 ======
|
|
|
|
/// <summary>获取 multiSelectComboBox1 选中的类型列表(元件/壳体/母排)。</summary>
|
|
private List<string> GetSelectedTypes()
|
|
{
|
|
var result = new List<string>();
|
|
var valsList = this.multiSelectComboBox1.SelectedValues;
|
|
if (valsList != null) foreach (var v in valsList) if (!string.IsNullOrEmpty(v)) result.Add(v);
|
|
return result;
|
|
}
|
|
|
|
/// <summary>根据系数列名从行中解析原系数值(空值当 1.0)。</summary>
|
|
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; // 默认值
|
|
}
|
|
|
|
/// <summary>按"等于/加/减"方法应用数值。</summary>
|
|
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;";
|
|
}
|
|
|
|
/// <summary>查表索引(复用连接版本)。</summary>
|
|
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 ""; }
|
|
}
|
|
}
|
|
}
|