基线:全量源码首提(D1 版本控制落地,含第一轮优化 B1-B4/A1/A4/缩放修复)
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user