using System; using System.Collections.Generic; using System.Data; using System.Windows.Forms; namespace 成套报价软件 { /// /// 元件排序 的交互逻辑(事件处理)。 /// UI 布局定义见 元件排序.ui.cs。 /// /// 用法:MainForm button33「元件排序」传入(项目编号,当前图号,当前箱号)弹出。 /// 左侧点箱号 → 右侧加载该箱号元件;选中行用 置顶/上移/下移/置底 调整; /// 点「确定」把右侧列表顺序重排序号写回数据库;「取消」不保存。 /// public partial class 元件排序 { // ===== 主窗口传入的上下文 ===== /// 当前项目编号。 private string _projectId = ""; /// 打开时的当前图号(用于默认选中箱号)。 private string _initTuhao = ""; /// 打开时的当前箱号(用于默认选中)。 private string _initXianghao = ""; // ===== 左侧箱号列表数据 ===== /// 箱号列表数据:Key="图号内部ID\n箱号内部ID", Value="图号 / 箱号"(显示)。 private List> _boxList = new List>(); // ===== 右侧元件列表数据 ===== /// 当前箱号的元件行(独立 DataTable,不动主窗口内存缓存)。 private List _rows = new List(); /// 按元件名称分组后的组列表:每组=同名称的连续行(保持库内相对顺序)。 /// 右侧列表只显示每组一个名称(带数量),移动按钮按组整体移动。 private List> _groups = new List>(); /// _rows 所属的 DataTable(TableName=真实元件表名 yj_N,保存时用)。 private DataTable _table = null; /// 构造函数:初始化 UI 并绑定事件。 public 元件排序() { InitializeComponent(); BindEvents(); } /// 主窗口调用:传入项目上下文并默认选中当前箱号。 public 元件排序(string projectId, string currentTuhao, string currentXianghao) : this() { _projectId = projectId ?? ""; _initTuhao = currentTuhao ?? ""; _initXianghao = currentXianghao ?? ""; } /// 绑定事件 + 初始状态。 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; } /// 窗体加载:装载箱号列表并默认选中当前箱号。 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); } } // ============================================================ // 数据装载 // ============================================================ /// 报价库路径(与MainForm GetQuoteDbPath 同规则)。 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;"; } /// 读取当前项目所有图号下的箱号,填充左侧列表。 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>(); 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(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( th.Key + "\n" + xid, th.Value + " / " + xnm)); } } } catch { } } } foreach (var kv in _boxList) this.listBoxXianghao.Items.Add(kv.Value); } /// 查图号的箱号真实表名(xh_N),无则返回空。 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(); } } /// 查箱号的元件真实表名(yj_N),无则返回空。 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(); } } // ============================================================ // 左侧箱号 → 右侧元件 // ============================================================ /// 点左侧箱号:加载该箱号的元件到右侧列表。 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); } /// 右侧列表是否有未保存的顺序改动。 private bool _dirty = false; /// /// 把 _rows 按「元件名称」分组到 _groups: /// 同名称的行归为一组(组内保持库内相对顺序),右侧列表每组只显示一行。 /// private void BuildGroups() { _groups.Clear(); if (_rows.Count == 0) return; // 名称 → 组 var map = new Dictionary>(); var order = new List(); // 首次出现顺序 foreach (DataRow r in _rows) { string name = _table.Columns.Contains("元件名称") ? (r["元件名称"] ?? "").ToString() : ""; if (string.IsNullOrWhiteSpace(name)) name = "(未命名)"; List g; if (!map.TryGetValue(name, out g)) { g = new List(); map[name] = g; order.Add(name); } g.Add(r); } foreach (string nm in order) _groups.Add(map[nm]); } /// 刷新右侧元件列表:每组一行,只显示元件名称(同名多行带 ×N)。 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; } } // ============================================================ // 上移/下移/置顶/置底(按组整体移动) // ============================================================ /// 移动右侧选中的组:delta=-1上移/+1下移/±int.MaxValue置顶置底。 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 moved = _groups[idx]; _groups.RemoveAt(idx); _groups.Insert(target, moved); _dirty = true; RefreshYuanjianList(target); } // ============================================================ // 保存 // ============================================================ /// 确定:把当前箱号顺序写库,然后关窗。 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; } } /// /// 按 _groups 顺序展开成新 _rows,重排序号并整表写回库。 /// 组内保持库内原相对顺序;主窗口关闭窗体后会清缓存重载,报价页自动看到新顺序。 /// private void SaveCurrentOrder(bool showMessage) { if (_table == null || _groups.Count == 0) return; // 按组顺序展开成新的行顺序 _rows.Clear(); foreach (List 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(); foreach (DataColumn c in ordered.Columns) cols.Add(c.ColumnName); var colParts = new List(); var valParts = new List(); 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); } } } }