Files

448 lines
18 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Data;
using System.Windows.Forms;
namespace 成套报价软件
{
/// <summary>
/// 元件排序 的交互逻辑(事件处理)。
/// UI 布局定义见 元件排序.ui.cs。
///
/// 用法:MainForm button33「元件排序」传入(项目编号,当前图号,当前箱号)弹出。
/// 左侧点箱号 → 右侧加载该箱号元件;选中行用 置顶/上移/下移/置底 调整;
/// 点「确定」把右侧列表顺序重排序号写回数据库;「取消」不保存。
/// </summary>
public partial class 元件排序
{
// ===== 主窗口传入的上下文 =====
/// <summary>当前项目编号。</summary>
private string _projectId = "";
/// <summary>打开时的当前图号(用于默认选中箱号)。</summary>
private string _initTuhao = "";
/// <summary>打开时的当前箱号(用于默认选中)。</summary>
private string _initXianghao = "";
// ===== 左侧箱号列表数据 =====
/// <summary>箱号列表数据:Key="图号内部ID\n箱号内部ID", Value="图号 / 箱号"(显示)。</summary>
private List<KeyValuePair<string, string>> _boxList = new List<KeyValuePair<string, string>>();
// ===== 右侧元件列表数据 =====
/// <summary>当前箱号的元件行(独立 DataTable,不动主窗口内存缓存)。</summary>
private List<DataRow> _rows = new List<DataRow>();
/// <summary>按元件名称分组后的组列表:每组=同名称的连续行(保持库内相对顺序)。
/// 右侧列表只显示每组一个名称(带数量),移动按钮按组整体移动。</summary>
private List<List<DataRow>> _groups = new List<List<DataRow>>();
/// <summary>_rows 所属的 DataTable(TableName=真实元件表名 yj_N,保存时用)。</summary>
private DataTable _table = null;
/// <summary>构造函数:初始化 UI 并绑定事件。</summary>
public 元件排序()
{
InitializeComponent();
BindEvents();
}
/// <summary>主窗口调用:传入项目上下文并默认选中当前箱号。</summary>
public 元件排序(string projectId, string currentTuhao, string currentXianghao)
: this()
{
_projectId = projectId ?? "";
_initTuhao = currentTuhao ?? "";
_initXianghao = currentXianghao ?? "";
}
/// <summary>绑定事件 + 初始状态。</summary>
private void BindEvents()
{
// 左侧箱号 → 加载元件
this.listBoxXianghao.SelectedIndexChanged += listBoxXianghao_SelectedIndexChanged;
// 移动按钮
this.btnMoveTop.Click += (s, e) => MoveSelected(-int.MaxValue);
this.btnMoveUp.Click += (s, e) => MoveSelected(-1);
this.btnMoveDown.Click += (s, e) => MoveSelected(1);
this.btnMoveBottom.Click += (s, e) => MoveSelected(int.MaxValue);
// 确定按钮:DialogResult=OK 会直接关窗,在 Closing 前拦截保存
this.btnOK.Click += btnOK_Click;
}
/// <summary>窗体加载:装载箱号列表并默认选中当前箱号。</summary>
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
try
{
LoadBoxList();
// 默认选中当前箱号(显示名含 "图号 / 箱号",按箱号内部ID 匹配)
int sel = -1;
if (!string.IsNullOrEmpty(_initXianghao))
{
for (int i = 0; i < _boxList.Count; i++)
{
if (_boxList[i].Key.EndsWith("\n" + _initXianghao))
{
sel = i;
break;
}
}
}
if (sel < 0 && _boxList.Count > 0) sel = 0;
if (sel >= 0) this.listBoxXianghao.SelectedIndex = sel;
}
catch (Exception ex)
{
MessageBox.Show(this, "加载箱号失败:\n" + ex.Message, "元件排序",
MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
// ============================================================
// 数据装载
// ============================================================
/// <summary>报价库路径(与MainForm GetQuoteDbPath 同规则)。</summary>
private string QuoteDbPath
{
get
{
string safe = _projectId;
foreach (char c in System.IO.Path.GetInvalidFileNameChars()) safe = safe.Replace(c, '_');
return System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory,
@"sjk\bjb\项目" + safe + ".db");
}
}
private static string ConnStr(string dbPath) { return "Data Source=" + dbPath + ";Version=3;"; }
/// <summary>读取当前项目所有图号下的箱号,填充左侧列表。</summary>
private void LoadBoxList()
{
_boxList.Clear();
this.listBoxXianghao.Items.Clear();
string dbPath = QuoteDbPath;
if (!System.IO.File.Exists(dbPath)) return;
using (var conn = new System.Data.SQLite.SQLiteConnection(ConnStr(dbPath)))
{
conn.Open();
// 图号表:内部ID + 图号
var tuhaos = new List<KeyValuePair<string, string>>();
using (var cmd = new System.Data.SQLite.SQLiteCommand(
"SELECT 内部ID, 图号 FROM advancedDataGridView9Table ORDER BY rowid;", conn))
using (var rd = cmd.ExecuteReader())
{
while (rd.Read())
{
string id = rd.IsDBNull(0) ? "" : rd.GetString(0);
string nm = rd.IsDBNull(1) ? "" : rd.GetString(1);
if (!string.IsNullOrEmpty(id))
tuhaos.Add(new KeyValuePair<string, string>(id, nm));
}
}
// 每个图号 → 查表索引拿箱号表 → 读箱号
foreach (var th in tuhaos)
{
string xhTbl = GetXianghaoTable(conn, th.Key);
if (string.IsNullOrEmpty(xhTbl)) continue;
try
{
using (var cmd = new System.Data.SQLite.SQLiteCommand(
"SELECT 内部ID, 箱号 FROM \"" + xhTbl + "\" ORDER BY rowid;", conn))
using (var rd = cmd.ExecuteReader())
{
while (rd.Read())
{
string xid = rd.IsDBNull(0) ? "" : rd.GetString(0);
string xnm = rd.IsDBNull(1) ? "" : rd.GetString(1);
if (string.IsNullOrEmpty(xid)) continue;
_boxList.Add(new KeyValuePair<string, string>(
th.Key + "\n" + xid, th.Value + " / " + xnm));
}
}
}
catch { }
}
}
foreach (var kv in _boxList) this.listBoxXianghao.Items.Add(kv.Value);
}
/// <summary>查图号的箱号真实表名(xh_N),无则返回空。</summary>
private static string GetXianghaoTable(System.Data.SQLite.SQLiteConnection conn, string tuhaoId)
{
using (var cmd = new System.Data.SQLite.SQLiteCommand(
"SELECT 实际表名 FROM 表索引 WHERE 类型='箱号' AND 图号=@th LIMIT 1;", conn))
{
cmd.Parameters.AddWithValue("@th", tuhaoId);
object o = cmd.ExecuteScalar();
return (o == null || o == DBNull.Value) ? "" : o.ToString();
}
}
/// <summary>查箱号的元件真实表名(yj_N),无则返回空。</summary>
private static string GetYuanjianTable(System.Data.SQLite.SQLiteConnection conn,
string tuhaoId, string xianghaoId)
{
using (var cmd = new System.Data.SQLite.SQLiteCommand(
"SELECT 实际表名 FROM 表索引 WHERE 类型='元件' AND 图号=@th AND 箱号=@xh LIMIT 1;", conn))
{
cmd.Parameters.AddWithValue("@th", tuhaoId);
cmd.Parameters.AddWithValue("@xh", xianghaoId);
object o = cmd.ExecuteScalar();
return (o == null || o == DBNull.Value) ? "" : o.ToString();
}
}
// ============================================================
// 左侧箱号 → 右侧元件
// ============================================================
/// <summary>点左侧箱号:加载该箱号的元件到右侧列表。</summary>
private void listBoxXianghao_SelectedIndexChanged(object sender, EventArgs e)
{
int idx = this.listBoxXianghao.SelectedIndex;
if (idx < 0 || idx >= _boxList.Count) return;
// 切箱号前,如果当前箱号有未保存的顺序改动 → 先保存
if (_table != null && _dirty) SaveCurrentOrder(false);
string[] parts = _boxList[idx].Key.Split('\n');
if (parts.Length != 2) return;
string yjTbl;
using (var conn = new System.Data.SQLite.SQLiteConnection(ConnStr(QuoteDbPath)))
{
conn.Open();
yjTbl = GetYuanjianTable(conn, parts[0], parts[1]);
}
_rows.Clear();
_groups.Clear();
_table = null;
this.listBoxYuanjian.Items.Clear();
if (string.IsNullOrEmpty(yjTbl))
{
// 该箱号还没有元件表
this.listBoxYuanjian.Items.Add("(此箱号暂无元件)");
return;
}
// 读成独立 DataTable(按 rowid = 原始顺序)
var t = new DataTable();
t.TableName = yjTbl;
using (var conn = new System.Data.SQLite.SQLiteConnection(ConnStr(QuoteDbPath)))
{
conn.Open();
using (var cmd = new System.Data.SQLite.SQLiteCommand(
"SELECT * FROM \"" + yjTbl + "\" ORDER BY rowid;", conn))
using (var adp = new System.Data.SQLite.SQLiteDataAdapter(cmd))
{
adp.Fill(t);
}
}
_table = t;
foreach (DataRow r in t.Rows) _rows.Add(r);
BuildGroups();
RefreshYuanjianList(-1);
}
/// <summary>右侧列表是否有未保存的顺序改动。</summary>
private bool _dirty = false;
/// <summary>
/// 把 _rows 按「元件名称」分组到 _groups:
/// 同名称的行归为一组(组内保持库内相对顺序),右侧列表每组只显示一行。
/// </summary>
private void BuildGroups()
{
_groups.Clear();
if (_rows.Count == 0) return;
// 名称 → 组
var map = new Dictionary<string, List<DataRow>>();
var order = new List<string>(); // 首次出现顺序
foreach (DataRow r in _rows)
{
string name = _table.Columns.Contains("元件名称") ? (r["元件名称"] ?? "").ToString() : "";
if (string.IsNullOrWhiteSpace(name)) name = "(未命名)";
List<DataRow> g;
if (!map.TryGetValue(name, out g))
{
g = new List<DataRow>();
map[name] = g;
order.Add(name);
}
g.Add(r);
}
foreach (string nm in order) _groups.Add(map[nm]);
}
/// <summary>刷新右侧元件列表:每组一行,只显示元件名称(同名多行带 ×N)。</summary>
private void RefreshYuanjianList(int keepIndex)
{
this.listBoxYuanjian.Items.Clear();
for (int i = 0; i < _groups.Count; i++)
{
string name = _table.Columns.Contains("元件名称")
? (_groups[i][0]["元件名称"] ?? "").ToString() : "";
if (string.IsNullOrWhiteSpace(name)) name = "(未命名)";
string text = (i + 1).ToString() + ". " + name;
if (_groups[i].Count > 1) text += " ×" + _groups[i].Count;
this.listBoxYuanjian.Items.Add(text);
}
if (this.listBoxYuanjian.Items.Count > 0)
{
int sel = keepIndex >= 0 && keepIndex < this.listBoxYuanjian.Items.Count ? keepIndex : 0;
this.listBoxYuanjian.SelectedIndex = sel;
}
}
// ============================================================
// 上移/下移/置顶/置底(按组整体移动)
// ============================================================
/// <summary>移动右侧选中的组:delta=-1上移/+1下移/±int.MaxValue置顶置底。</summary>
private void MoveSelected(int delta)
{
if (_groups.Count < 2) return;
int idx = this.listBoxYuanjian.SelectedIndex;
if (idx < 0) return;
int target;
if (delta == -int.MaxValue) target = 0;
else if (delta == int.MaxValue) target = _groups.Count - 1;
else target = idx + delta;
if (target < 0 || target >= _groups.Count || target == idx) return;
List<DataRow> moved = _groups[idx];
_groups.RemoveAt(idx);
_groups.Insert(target, moved);
_dirty = true;
RefreshYuanjianList(target);
}
// ============================================================
// 保存
// ============================================================
/// <summary>确定:把当前箱号顺序写库,然后关窗。</summary>
private void btnOK_Click(object sender, EventArgs e)
{
try
{
if (_table != null && _dirty)
{
SaveCurrentOrder(true);
}
// DialogResult 已由按钮属性设为 OK,直接关窗
}
catch (Exception ex)
{
// 保存失败则不关窗,让用户决定
MessageBox.Show(this, "保存排序失败:\n" + ex.Message, "元件排序",
MessageBoxButtons.OK, MessageBoxIcon.Error);
this.DialogResult = DialogResult.None;
}
}
/// <summary>
/// 按 _groups 顺序展开成新 _rows,重排序号并整表写回库。
/// 组内保持库内原相对顺序;主窗口关闭窗体后会清缓存重载,报价页自动看到新顺序。
/// </summary>
private void SaveCurrentOrder(bool showMessage)
{
if (_table == null || _groups.Count == 0) return;
// 按组顺序展开成新的行顺序
_rows.Clear();
foreach (List<DataRow> g in _groups) _rows.AddRange(g);
// 重排序号 1..N
if (_table.Columns.Contains("序号"))
{
for (int i = 0; i < _rows.Count; i++)
_rows[i]["序号"] = (i + 1).ToString();
}
// 按新顺序重建表
var ordered = _table.Clone();
ordered.TableName = _table.TableName;
foreach (DataRow r in _rows) ordered.ImportRow(r);
string dbPath = QuoteDbPath;
using (var conn = new System.Data.SQLite.SQLiteConnection(ConnStr(dbPath)))
{
conn.Open();
using (var tx = conn.BeginTransaction())
{
try
{
// 删旧 + 按新顺序插回
using (var del = new System.Data.SQLite.SQLiteCommand(
"DELETE FROM \"" + _table.TableName + "\";", conn, tx))
{
del.ExecuteNonQuery();
}
// 用参数化 INSERT(列以表实际列为准)
var cols = new List<string>();
foreach (DataColumn c in ordered.Columns) cols.Add(c.ColumnName);
var colParts = new List<string>();
var valParts = new List<string>();
for (int ci = 0; ci < cols.Count; ci++)
{
colParts.Add("\"" + cols[ci] + "\"");
valParts.Add("@p" + ci);
}
string sql = "INSERT INTO \"" + _table.TableName + "\" (" +
string.Join(",", colParts.ToArray()) +
") VALUES (" + string.Join(",", valParts.ToArray()) + ");";
using (var ins = new System.Data.SQLite.SQLiteCommand(sql, conn, tx))
{
for (int ci = 0; ci < cols.Count; ci++)
ins.Parameters.Add("@p" + ci, System.Data.DbType.String);
foreach (DataRow r in ordered.Rows)
{
for (int i = 0; i < ordered.Columns.Count; i++)
ins.Parameters[i].Value = (r[i] == null || r[i] == DBNull.Value)
? (object)"" : r[i].ToString();
ins.ExecuteNonQuery();
}
}
tx.Commit();
}
catch
{
try { tx.Rollback(); } catch { }
throw;
}
}
}
_dirty = false;
if (showMessage)
{
MessageBox.Show(this, "排序已保存!\n\n共 " + ordered.Rows.Count + " 行元件已按新顺序写入。",
"元件排序", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
}
}
}