基线:全量源码首提(D1 版本控制落地,含第一轮优化 B1-B4/A1/A4/缩放修复)

This commit is contained in:
dqb-dev
2026-10-01 22:15:22 +08:00
commit 1e65792b09
499 changed files with 204579 additions and 0 deletions
+232
View File
@@ -0,0 +1,232 @@
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace 成套报价软件
{
/// <summary>
/// 新建项目 的交互逻辑(事件处理)。
/// UI 布局定义见 新建项目.ui.cs。
/// 本窗体作为 MainForm 的对话框使用,故不含独立的 Main 入口点。
/// </summary>
public partial class 新建项目 : Form
{
/// <summary>项目数据库文件相对路径(与 MainForm 保持一致)。</summary>
private readonly string _dbPath;
/// <summary>项目数据库表名。</summary>
private readonly string _tableName;
/// <summary>构造函数:初始化 UI 并绑定事件。</summary>
public 新建项目()
: this(null, null)
{
// 无参构造保留,以便独立测试时仍可创建。
}
/// <summary>
/// 带数据库路径的构造函数(主窗口调用此版本)。
/// 传入数据库后,对话框会在打开时自动填入“项目日期”和“项目编号”。
/// </summary>
/// <param name="dbPath">SQLite 数据库绝对路径;为 null/不存在则不自动生成编号。</param>
/// <param name="tableName">项目数据表名。</param>
public 新建项目(string dbPath, string tableName)
{
InitializeComponent();
_dbPath = dbPath;
_tableName = tableName;
// 预置价格格式化下拉框选项(小数位 + 取整方式)
InitPriceFormatCombos();
// 加载完成后(已显示)自动填充编号和日期。
// 用 Load 事件确保控件都已创建好。
this.Load += 新建项目_Load;
}
/// <summary>
/// 预置“单台价格/项目总价/单台取整/项目取整”4 个下拉框的“候选选项列表”。
/// 注意:只填充 Items,不改动 SelectedIndex/Text —— 默认选中项由设计器 XML 里的
/// Text 属性决定,不要在代码里覆盖。
///
/// 控件布局对应(UI 里 label 文字决定语义):
/// 单台价格行 → cmb单台价格(单台层小数位)
/// 项目总价行 → cmb项目总价(项目层小数位)
/// 单台取整行 → comboBox1 (单台层取整方式)
/// 项目取整行 → comboBox2 (项目层取整方式)
/// </summary>
private void InitPriceFormatCombos()
{
// 小数位选项:cmb单台价格(单台层) + cmb项目总价(项目层)
this.cmb单台价格.Items.Clear();
this.cmb单台价格.Items.AddRange(价格格式化.小数位选项);
this.cmb项目总价.Items.Clear();
this.cmb项目总价.Items.AddRange(价格格式化.小数位选项);
// 取整方式选项:comboBox1(单台层) + comboBox2(项目层)
this.comboBox1.Items.Clear();
this.comboBox1.Items.AddRange(价格格式化.取整方式选项);
this.comboBox2.Items.Clear();
this.comboBox2.Items.AddRange(价格格式化.取整方式选项);
}
/// <summary>对话框首次加载:自动填项目日期 + 项目编号。</summary>
private void 新建项目_Load(object sender, EventArgs e)
{
FillAutoFields();
}
/// <summary>
/// 自动填充“项目日期”为当天,并按 yyyyMMdd+3位序号
/// 规则生成“项目编号”(序号=数据库当天最大序号+1)。
/// 同时把 txt项目编号 设为只读,用户不能修改。
/// </summary>
private void FillAutoFields()
{
// 1) 项目日期 = 当天 yyyy-MM-dd
DateTime today = DateTime.Today;
this.txt项目日期.Text = today.ToString("yyyy-MM-dd");
// 2) 项目编号 = yyyyMMdd + 3 位序号(001 起,查库取当天最大序号 +1)
string datePart = today.ToString("yyyyMMdd");
int seq = GetNextSeqForDate(datePart); // 找不到就返回 1
this.txt项目编号.Text = datePart + seq.ToString("000");
// 3) 项目编号只读,用户不能改
this.txt项目编号.ReadOnly = true;
this.txt项目编号.TabStop = false;
// 给只读框一个浅灰背景,视觉上提示“不可编辑”
this.txt项目编号.BackColor = System.Drawing.SystemColors.Control;
// 4) 成套厂信息(名称/地址/联系人/联系电话)从「企业信息」配置自动带入,
// 用户可在对话框中临时改写;改写后的值随项目一起保存到项目库。
// 若企业信息未配置,则留空由用户手填。
try
{
var qiye = 企业信息.LoadSettings();
if (qiye != null)
{
// 仅在文本框为空时填入,避免覆盖已有内容(理论上 Load 阶段都是空)
if (string.IsNullOrEmpty(this.txt成套厂名称.Text))
this.txt成套厂名称.Text = qiye["企业名称"];
if (string.IsNullOrEmpty(this.txt成套厂地址.Text))
this.txt成套厂地址.Text = qiye["地址"];
if (string.IsNullOrEmpty(this.txt成套厂联系人.Text))
this.txt成套厂联系人.Text = qiye["联系人"];
if (string.IsNullOrEmpty(this.txt成套厂联系电话.Text))
this.txt成套厂联系电话.Text = qiye["电话"];
}
}
catch
{
// 读不到企业信息不阻断新建项目流程
}
}
/// <summary>
/// 在数据库中查找“指定日期前缀(yyyyMMdd)”的最大序号,
/// 返回“最大序号 + 1”;若无数据或出错则返回 1。
/// 例:库中已有 20260721001、20260721005,返回 6。
/// </summary>
private int GetNextSeqForDate(string datePart)
{
if (string.IsNullOrEmpty(datePart) ||
string.IsNullOrEmpty(_dbPath) ||
string.IsNullOrEmpty(_tableName) ||
!System.IO.File.Exists(_dbPath))
{
return 1;
}
try
{
string connStr = "Data Source=" + _dbPath + ";Version=3;";
using (var conn = new System.Data.SQLite.SQLiteConnection(connStr))
{
conn.Open();
// 取“项目编号”这一列中、前 8 位等于 datePart 的最大值。
// SUBSTR 兼容 SQLite,从 1 开始计数,截取前 8 位。
string sql =
"SELECT MAX(CAST(SUBSTR(项目编号, 9) AS INTEGER)) " +
"FROM " + _tableName + " " +
"WHERE SUBSTR(项目编号, 1, 8) = '" + datePart + "';";
using (var cmd = new System.Data.SQLite.SQLiteCommand(sql, conn))
{
object result = cmd.ExecuteScalar();
if (result == null || result == DBNull.Value)
{
return 1;
}
int maxSeq;
// ExecuteScalar 返回的可能是 long/int
if (int.TryParse(result.ToString(), out maxSeq))
{
return maxSeq + 1;
}
return 1;
}
}
}
catch
{
// 查询失败时不阻断流程,退化为从 001 开始
return 1;
}
}
/// <summary>
/// 收集对话框中已填写的字段,以“去掉 txt 前缀的字段名”为键返回。
/// 仅包含实际存在文本框且文本非空的字段。
/// 调用方只需按列名(Name/DataPropertyName)匹配,匹配不上的字段自动忽略。
/// </summary>
public Dictionary<string, string> GetInputValues()
{
var values = new Dictionary<string, string>();
// (字段名, 对应文本框)。字段名与MainForm advancedDataGridView6 列名一致。
AddIfNotEmpty(values, "项目编号", txt项目编号.Text);
AddIfNotEmpty(values, "项目日期", txt项目日期.Text);
AddIfNotEmpty(values, "项目名称", txt项目名称.Text);
AddIfNotEmpty(values, "客户名称", txt客户名称.Text);
AddIfNotEmpty(values, "客户地址", txt客户地址.Text);
AddIfNotEmpty(values, "联系人", txt联系人.Text);
AddIfNotEmpty(values, "联系电话", txt联系电话.Text);
AddIfNotEmpty(values, "备注", txt备注.Text);
// 成套厂信息(4 个字段):新建项目时从企业信息带入,用户可改写;
// 写入项目库,供项目窗口后续调用。
AddIfNotEmpty(values, "成套厂名称", txt成套厂名称.Text);
AddIfNotEmpty(values, "成套厂地址", txt成套厂地址.Text);
AddIfNotEmpty(values, "成套厂联系人", txt成套厂联系人.Text);
AddIfNotEmpty(values, "成套厂联系电话", txt成套厂联系电话.Text);
// 价格显示格式化(4 个字段):控制报价页价格单元格的“小数位 + 取整方式”。
// 仅影响显示,不改变原始计算值;随项目一起保存到「项目信息」表。
// 控件对应:cmb单台价格=单台小数位 / cmb项目总价=项目小数位 /
// comboBox1=单台取整方式 / comboBox2=项目取整方式
values["单台小数位"] = (cmb单台价格.SelectedItem ?? cmb单台价格.Text ?? "").ToString();
values["项目小数位"] = (cmb项目总价.SelectedItem ?? cmb项目总价.Text ?? "").ToString();
values["单台取整方式"] = (comboBox1.SelectedItem ?? comboBox1.Text ?? "").ToString();
values["项目取整方式"] = (comboBox2.SelectedItem ?? comboBox2.Text ?? "").ToString();
return values;
}
private static void AddIfNotEmpty(Dictionary<string, string> dict, string key, string value)
{
if (!string.IsNullOrWhiteSpace(value))
{
dict[key] = value.Trim();
}
}
// 提示:在设计器的属性窗口(事件页)双击事件名,
// 会自动在此生成事件处理方法骨架。
}
}