基线:全量源码首提(D1 版本控制落地,含第一轮优化 B1-B4/A1/A4/缩放修复)
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 诊断:无明细箱号导出问题(临时)
|
||||
import sqlite3
|
||||
|
||||
db = r'C:\成套报价软件\sjk\bjb\项目20260920001.db'
|
||||
c = sqlite3.connect(db)
|
||||
|
||||
print('=== xh_1 箱号行(箱号/箱柜类型/数量/单台成本/成本价/单台报出/报出价):')
|
||||
try:
|
||||
cols = [r[1] for r in c.execute('PRAGMA table_info(xh_1)')]
|
||||
print('列:', cols)
|
||||
for r in c.execute('SELECT 箱号,箱柜类型,数量,单台成本,成本价,单台报出,报出价 FROM xh_1'):
|
||||
print(' ', r)
|
||||
except Exception as e:
|
||||
print('读失败:', e)
|
||||
|
||||
print('=== 表索引(元件):')
|
||||
for r in c.execute("SELECT 图号,箱号,实际表名 FROM 表索引 WHERE 类型='元件'"):
|
||||
print(' ', r)
|
||||
|
||||
print('=== 图号表箱号内部ID:')
|
||||
for r in c.execute('SELECT 内部ID,箱号 FROM xh_1'):
|
||||
print(' ', r)
|
||||
|
||||
# 无明细箱号的元件表内容
|
||||
for (xh_id, nm) in c.execute('SELECT 内部ID,箱号 FROM xh_1').fetchall():
|
||||
tp = c.execute('SELECT 箱柜类型 FROM xh_1 WHERE 内部ID=?', (xh_id,)).fetchone()
|
||||
if tp and tp[0] in ('无明细', '明细'):
|
||||
t = c.execute("SELECT 实际表名 FROM 表索引 WHERE 类型='元件' AND 箱号=?", (xh_id,)).fetchone()
|
||||
print('=== 无明细箱号[%s] 内部ID=%s 元件表=%s' % (nm, xh_id, t))
|
||||
if t:
|
||||
for r in c.execute('SELECT 序号,元件名称,表价,报出价 FROM "%s"' % t[0]):
|
||||
print(' ', r)
|
||||
@@ -0,0 +1,6 @@
|
||||
$names = 'System.Memory','System.Numerics.Vectors','System.Runtime.CompilerServices.Unsafe','System.Resources.Extensions','Smart.CustomComponents','System.Data.SQLite','WeifenLuo.WinFormsUI.Docking','WeifenLuo.WinFormsUI.Docking.ThemeVS2015'
|
||||
foreach ($n in $names) {
|
||||
$p = Join-Path 'C:\Program Files (x86)\SharpDevelop\5.2\bin' ($n + '.dll')
|
||||
$an = [System.Reflection.AssemblyName]::GetAssemblyName($p)
|
||||
Write-Output ($an.FullName)
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 对比源 ku 与 release ku 的文件清单,确认差异仅为 __pycache__/*.pyc 排除
|
||||
import os
|
||||
|
||||
SRC = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\ku"
|
||||
DST = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\release\成套报价软件\ku"
|
||||
|
||||
def scan(root):
|
||||
result = set()
|
||||
for dirpath, dirnames, filenames in os.walk(root):
|
||||
rel = os.path.relpath(dirpath, root)
|
||||
for f in filenames:
|
||||
p = os.path.normpath(os.path.join(rel, f))
|
||||
result.add(p)
|
||||
return result
|
||||
|
||||
src = scan(SRC)
|
||||
dst = scan(DST)
|
||||
|
||||
only_src = sorted(src - dst)
|
||||
only_dst = sorted(dst - src)
|
||||
|
||||
pycache_related = [p for p in only_src if "__pycache__" in p or p.endswith(".pyc")]
|
||||
other_src = [p for p in only_src if p not in pycache_related]
|
||||
|
||||
print("源文件数:", len(src), " release文件数:", len(dst))
|
||||
print("仅源有(共%d): __pycache__/.pyc 类 %d 个, 其他 %d 个" % (len(only_src), len(pycache_related), len(other_src)))
|
||||
for p in other_src[:20]:
|
||||
print(" [缺失!]", p)
|
||||
print("仅release有(共%d):" % len(only_dst))
|
||||
for p in only_dst[:20]:
|
||||
print(" [多余!]", p)
|
||||
if not other_src and not only_dst:
|
||||
print("\n结论: 差异全部是 __pycache__/*.pyc 排除(make_package.py 刻意设计), .venv 完整")
|
||||
@@ -0,0 +1,213 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
精简 Designer.cs:只保留设计器需要的最小属性集,
|
||||
去掉旧工具写入的全量属性垃圾(几十行默认值/无效值),
|
||||
解决 SharpDevelop 设计时 ArgumentOutOfRangeException。
|
||||
"""
|
||||
import io
|
||||
import re
|
||||
import os
|
||||
import glob
|
||||
|
||||
BASE = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
|
||||
|
||||
# 允许保留的属性行前缀模式
|
||||
KEEP_PROPS = [
|
||||
# ★ 控件创建语句(new) — 绝对不能删
|
||||
r"this\.\w+\s*=\s*new",
|
||||
r"\w+\s*=\s*new",
|
||||
r"new\s+System\.Windows\.Forms\.",
|
||||
r"new\s+Smart\.CustomComponents\.",
|
||||
r"new\s+Zuby\.ADGV\.",
|
||||
r"new\s+CJZZ\.Controls\.",
|
||||
r"new\s+System\.Drawing\.",
|
||||
r"new\s+System\.ComponentModel\.",
|
||||
# 列字段创建
|
||||
r"dataGridViewTextBoxColumn\d+\s*=",
|
||||
r"dataGridViewCheckBoxColumn\d+\s*=",
|
||||
r"dataGridViewComboBoxColumn\d+\s*=",
|
||||
r"System\.Windows\.Forms\.DataGridView\w+Column",
|
||||
r"this\.\w+\.Name\s*=",
|
||||
r"this\.\w+\.Size\s*=",
|
||||
r"this\.\w+\.Location\s*=",
|
||||
r"this\.\w+\.Text\s*=",
|
||||
r"this\.\w+\.TabIndex\s*=",
|
||||
r"this\.\w+\.Dock\s*=",
|
||||
r"this\.\w+\.Anchor\s*=",
|
||||
r"this\.\w+\.Font\s*=",
|
||||
r"this\.\w+\.BackColor\s*=",
|
||||
r"this\.\w+\.ForeColor\s*=",
|
||||
r"this\.\w+\.Enabled\s*=",
|
||||
r"this\.\w+\.Visible\s*=",
|
||||
r"this\.\w+\.ReadOnly\s*=",
|
||||
r"this\.\w+\.Multiline\s*=",
|
||||
r"this\.\w+\.WordWrap\s*=",
|
||||
r"this\.\w+\.ScrollBars\s*=",
|
||||
r"this\.\w+\.SelectedIndex\s*=\s*[0-9]", # 只保留>=0的
|
||||
r"this\.\w+\.Items\.Add",
|
||||
r"this\.\w+\.Items\.AddRange",
|
||||
r"this\.\w+\.Items\.Clear",
|
||||
r"this\.\w+\.Columns\.Add",
|
||||
r"this\.\w+\.Columns\.AddRange",
|
||||
r"this\.\w+\.Rows\.Add",
|
||||
r"this\.\w+\.Controls\.Add",
|
||||
r"this\.\w+\.Controls\.AddRange",
|
||||
r"this\.\w+\.Controls\.Clear",
|
||||
r"this\.\w+\.HeaderText\s*=",
|
||||
r"this\.\w+\.Width\s*=",
|
||||
r"this\.\w+\.DataPropertyName\s*=",
|
||||
r"this\.\w+\.SortMode\s*=",
|
||||
r"this\.\w+\.ReadOnly\s*=",
|
||||
r"this\.\w+\.Frozen\s*=",
|
||||
r"this\.\w+\.AutoSizeMode\s*=\s*System\.Windows\.Forms\.DataGridViewAutoSizeMode",
|
||||
r"this\.\w+\.Visible\s*=",
|
||||
r"this\.\w+\.DefaultCellStyle\.",
|
||||
r"this\.\w+\.UseVisualStyleBackColor\s*=",
|
||||
r"this\.\w+\.Checked\s*=",
|
||||
r"this\.\w+\.CheckState\s*=",
|
||||
r"this\.\w+\.CheckOnClick\s*=",
|
||||
r"this\.\w+\.Style\s*=\s*Smart\.CustomComponents", # Smart 控件样式枚举
|
||||
r"this\.\w+\.Radius\s*=",
|
||||
r"this\.\w+\.PlaceholderText\s*=",
|
||||
r"this\.\w+\.MultiSelect\s*=",
|
||||
r"this\.\w+\.Searchable\s*=",
|
||||
r"this\.\w+\.AllowCustomInput\s*=",
|
||||
r"this\.\w+\.Flat\s*=",
|
||||
r"this\.\w+\.AllowUserToAddRows\s*=",
|
||||
r"this\.\w+\.AllowUserToDeleteRows\s*=",
|
||||
r"this\.\w+\.AllowUserToOrderColumns\s*=",
|
||||
r"this\.\w+\.AllowUserToResizeColumns\s*=",
|
||||
r"this\.\w+\.AllowUserToResizeRows\s*=",
|
||||
r"this\.\w+\.AutoSize\b",
|
||||
r"this\.\w+\.AutoScroll\s*=",
|
||||
r"this\.\w+\.RowHeadersVisible\s*=",
|
||||
r"this\.\w+\.ColumnHeadersVisible\s*=",
|
||||
r"this\.\w+\.SelectionMode\s*=",
|
||||
r"this\.\w+\.EditMode\s*=",
|
||||
r"this\.\w+\.BackgroundColor\s*=",
|
||||
r"this\.\w+\.EnableHeadersVisualStyles\s*=",
|
||||
r"this\.\w+\.EnableExcelStyleSelection\s*=",
|
||||
r"this\.\w+\.RowHeadersWidth\s*=",
|
||||
r"this\.\w+\.ColumnHeadersHeight\s*=",
|
||||
r"this\.\w+\.RowHeadersWidthSizeMode\s*=",
|
||||
r"this\.\w+\.ColumnHeadersHeightSizeMode\s*=",
|
||||
r"this\.\w+\.BorderStyle\s*=",
|
||||
r"this\.\w+\.CellBorderStyle\s*=",
|
||||
r"this\.\w+\.GridColor\s*=",
|
||||
r"this\.\w+\.ClipboardCopyMode\s*=",
|
||||
r"this\.\w+\.MultiSelect\s*=",
|
||||
r"this\.\w+\.StandardTab\s*=",
|
||||
r"this\.\w+\.VirtualMode\s*=",
|
||||
r"this\.\w+\.ImageKey\s*=",
|
||||
r"this\.\w+\.ImageScaling\s*=",
|
||||
r"this\.\w+\.DisplayStyle\s*=",
|
||||
r"this\.\w+\.TextAlign\s*=\s*System\.Drawing", # 只留 ContentAlignment
|
||||
r"this\.\w+\.TextDirection\s*=",
|
||||
r"this\.\w+\.ToolTipText\s*=",
|
||||
r"this\.\w+\.AutoSize\b.*=\s*true",
|
||||
# 窗体自身属性
|
||||
r"this\.AutoScaleDimensions\s*=",
|
||||
r"this\.AutoScaleMode\s*=",
|
||||
r"this\.ClientSize\s*=",
|
||||
r"this\.Name\s*=",
|
||||
r"this\.Text\s*=",
|
||||
r"this\.FormBorderStyle\s*=",
|
||||
r"this\.StartPosition\s*=",
|
||||
r"this\.WindowState\s*=",
|
||||
r"this\.MaximizeBox\s*=",
|
||||
r"this\.MinimizeBox\s*=",
|
||||
r"this\.ControlBox\s*=",
|
||||
r"this\.ShowInTaskbar\s*=",
|
||||
r"this\.TopMost\s*=",
|
||||
r"this\.Opacity\s*=",
|
||||
r"this\.ShowIcon\s*=",
|
||||
r"this\.Icon\s*=",
|
||||
r"this\.KeyPreview\s*=",
|
||||
r"this\.AutoValidate\s*=",
|
||||
r"this\.Padding\s*=\s*new\s+System\.Windows\.Forms\.Padding\([1-9]", # 只留非零Padding
|
||||
r"this\.MainMenuStrip\s*=",
|
||||
# 控件本身(非 this)的属性
|
||||
r"\w+\.Name\s*=",
|
||||
r"\w+\.HeaderText\s*=",
|
||||
r"\w+\.Width\s*=",
|
||||
r"\w+\.DataPropertyName\s*=",
|
||||
r"\w+\.SortMode\s*=",
|
||||
r"\w+\.ReadOnly\s*=",
|
||||
r"\w+\.Frozen\s*=",
|
||||
r"\w+\.Visible\s*=",
|
||||
r"\w+\.AutoSizeMode\s*=",
|
||||
r"\w+\.DefaultCellStyle\.",
|
||||
]
|
||||
|
||||
# 编译为正则列表
|
||||
KEEP_RE = [re.compile(p) for p in KEEP_PROPS]
|
||||
|
||||
# 绝对排除的(即使匹配 KEEP 也要删掉——已知导致设计器崩溃的)
|
||||
HARD_KILL = [
|
||||
r"this\.\w+\.Padding\s*=\s*new\s+System\.Windows\.Forms\.Padding\(0,\s*0,\s*0,\s*0\)",
|
||||
r"this\.\w+\.SelectedIndex\s*=\s*-1",
|
||||
r"this\.\w+\.DropDownWidth\s*=\s*0",
|
||||
r"this\.\w+\.DropDownHeight\s*=\s*0",
|
||||
r"this\.\w+\.MaximumSize\s*=\s*new\s+System\.Drawing\.Size\(0,\s*0\)",
|
||||
r"this\.\w+\.MinimumSize\s*=\s*new\s+System\.Drawing\.Size\(0,\s*0\)",
|
||||
r"this\.\w+\.Margin\s*=", # Margin 对设计器布局影响大,删掉
|
||||
]
|
||||
HARD_KILL_RE = [re.compile(p) for p in HARD_KILL]
|
||||
|
||||
|
||||
def should_keep(line):
|
||||
st = line.strip()
|
||||
if not st or st.startswith("//") or st.startswith("#"):
|
||||
return True # 注释空行保留
|
||||
# 不是赋值语句的直接保留(如 new, SuspendLayout, Controls.Add 等)
|
||||
if "=" not in st and ".Add" not in st and ".Clear" not in st:
|
||||
return True
|
||||
# 硬排除
|
||||
for kr in HARD_KILL_RE:
|
||||
if kr.search(st):
|
||||
return False
|
||||
# 白名单
|
||||
for kr in KEEP_RE:
|
||||
if kr.search(st):
|
||||
return True
|
||||
# 其他赋值语句 → 删掉
|
||||
if re.match(r"this\.\w+\.\w+\s*=", st) or re.match(r"\w+\.\w+\s*=", st):
|
||||
return False
|
||||
return True # 非赋值行保留
|
||||
|
||||
|
||||
def clean_file(path):
|
||||
s = io.open(path, encoding="utf-8-sig").read()
|
||||
lines = s.split("\n")
|
||||
out = []
|
||||
removed = 0
|
||||
for line in lines:
|
||||
if should_keep(line):
|
||||
out.append(line)
|
||||
else:
|
||||
removed += 1
|
||||
# 去掉连续空行
|
||||
result = []
|
||||
prev_blank = False
|
||||
for l in out:
|
||||
blank = l.strip() == ""
|
||||
if blank and prev_blank:
|
||||
continue
|
||||
result.append(l)
|
||||
prev_blank = blank
|
||||
s2 = "\n".join(result)
|
||||
if s2 != s:
|
||||
io.open(path, "w", encoding="utf-8-sig", newline="\r\n").write(s2)
|
||||
return removed
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
total = 0
|
||||
for pattern in ["xiangmu/*.Designer.cs", "shezhi/*.Designer.cs",
|
||||
"ku/*.Designer.cs", "GCS母排计算/*.Designer.cs"]:
|
||||
for f in glob.glob(os.path.join(BASE, pattern)):
|
||||
n = clean_file(f)
|
||||
total += n
|
||||
if n > 0:
|
||||
print("%s: 删除 %d 行冗余属性" % (os.path.basename(f), n))
|
||||
print("\n共删除 %d 行" % total)
|
||||
@@ -0,0 +1,54 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""同一张渲染 PNG 分别喂给 两个 Python 后端 的 ocr_worker,对比识别结果。"""
|
||||
import base64
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\pdfbt"
|
||||
IMG = ROOT + r"\bin\Debug\realtest\selftest_render.png"
|
||||
WORKER = ROOT + r"\bin\Debug\ocr_worker.py"
|
||||
PYENV = ROOT + r"\pyenv\python.exe"
|
||||
PYDEV = r"F:\PythonDev\python\python\python.exe"
|
||||
|
||||
def run_ocr(python_exe, label):
|
||||
with open(IMG, "rb") as f:
|
||||
b64 = base64.b64encode(f.read()).decode()
|
||||
p = subprocess.Popen([python_exe, "-u", WORKER],
|
||||
stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE, text=True, encoding="utf-8")
|
||||
# 等 warm+ready 两条就绪消息
|
||||
line1 = p.stdout.readline()
|
||||
line2 = p.stdout.readline()
|
||||
if 'ready' not in line2:
|
||||
# 兼容只发一条的情况
|
||||
if 'ready' not in line1 and 'warm' not in line1:
|
||||
print(label, '未就绪:', line1[:120], line2[:120]); p.kill(); return None
|
||||
req = json.dumps({"id": 1, "image": b64}, ensure_ascii=False)
|
||||
p.stdin.write(req + "\n")
|
||||
p.stdin.flush()
|
||||
resp = p.stdout.readline()
|
||||
p.kill()
|
||||
try:
|
||||
obj = json.loads(resp)
|
||||
except Exception as e:
|
||||
print(label, "解析失败:", e, "| 原始:", resp[:200])
|
||||
return None
|
||||
items = obj.get("items") or []
|
||||
texts = [it.get("text", "") for it in items]
|
||||
print("%s: %d 条, 耗时 %s ms" % (label, len(items), obj.get("ms")))
|
||||
return texts
|
||||
|
||||
t_pyenv = run_ocr(PYENV, "pyenv(CPU)")
|
||||
t_dev = run_ocr(PYDEV, "开发机(DML)")
|
||||
|
||||
if t_pyenv is not None and t_dev is not None:
|
||||
s1, s2 = set(t_pyenv), set(t_dev)
|
||||
only_env = [t for t in t_pyenv if t not in s2]
|
||||
only_dev = [t for t in t_dev if t not in s1]
|
||||
print("\n仅 pyenv 识别到(%d):" % len(only_env))
|
||||
for t in only_env[:10]:
|
||||
print(" +", t)
|
||||
print("仅 开发机 识别到(%d):" % len(only_dev))
|
||||
for t in only_dev[:10]:
|
||||
print(" -", t)
|
||||
@@ -0,0 +1,99 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""6 个 Fak* handler 数据源切换:fak.db → 方案窗口当前打开的方案文件。
|
||||
替换各 handler 内的路径定义为 方案数据源路径(),并把"方案库不存在"提示改为"请先打开方案"。
|
||||
另:恢复三组右键菜单挂接(完整功能),并加 方案数据源路径() helper。幂等。"""
|
||||
import io
|
||||
import re
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\主窗口.菜单与库.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
n = 0
|
||||
|
||||
# 0) helper 方法(挂在 ReloadFakData 后)
|
||||
helper = ''' /// <summary>方案窗口当前打开的方案文件路径(未打开返回 null)。</summary>
|
||||
private string 方案数据源路径()
|
||||
{
|
||||
return (this.w8 != null) ? this.w8.当前方案文件 : null;
|
||||
}
|
||||
|
||||
'''
|
||||
if "方案数据源路径()" not in s:
|
||||
anchor = " /// <summary>读方案库\"项目信息\"表第一行为字典(无表/无行返回空字典)。</summary>"
|
||||
assert anchor in s, "helper 锚点缺失"
|
||||
s = s.replace(anchor, helper + anchor, 1)
|
||||
n += 1
|
||||
|
||||
# 1) 6 个 handler 的路径定义+紧邻的存在校验/提示 → 方案数据源路径()+打开提示
|
||||
# 模式:string xxx = GetDbFullPath(@"sjk\fak.db");\n if (!File.Exists(xxx)) { 提示 return; }
|
||||
pat = re.compile(
|
||||
r"([ \t]+)string (fakDbPath|srcDbPath) = GetDbFullPath\(@\"sjk\\\\fak\.db\"\);\r?\n"
|
||||
r"[ \t]+if \(!System\.IO\.File\.Exists\(\2\)\)\r?\n"
|
||||
r"[ \t]+\{\r?\n"
|
||||
r"[ \t]+MessageBox\.Show\(this, \"([^\"]*)\",\r?\n"
|
||||
r"[ \t]+\"([^\"]*)\", MessageBoxButtons\.OK, MessageBoxIcon\.(Warning|Information)\);\r?\n"
|
||||
r"[ \t]+return;\r?\n"
|
||||
r"[ \t]+\}")
|
||||
|
||||
def sub_fn(m):
|
||||
global n
|
||||
n += 1
|
||||
ind, var, oldmsg, title, icon = m.group(1), m.group(2), m.group(3), m.group(4), m.group(5)
|
||||
return (
|
||||
ind + "string " + var + " = 方案数据源路径();\n" +
|
||||
ind + "if (string.IsNullOrEmpty(" + var + ") || !System.IO.File.Exists(" + var + "))\n" +
|
||||
ind + "{\n" +
|
||||
ind + " MessageBox.Show(this, \"请先在方案窗口的项目页双击打开一个方案,再进行此操作。\",\n" +
|
||||
ind + " \"" + title + "\", MessageBoxButtons.OK, MessageBoxIcon.Information);\n" +
|
||||
ind + " return;\n" +
|
||||
ind + "}"
|
||||
)
|
||||
|
||||
s, c = pat.subn(sub_fn, s)
|
||||
print("路径切换", c, "处")
|
||||
n += c
|
||||
|
||||
# 2) 恢复三组右键菜单挂接(若已被删)
|
||||
anchor2 = ''' // ---- 方案窗口(advancedDataGridView图号/箱号/元件)右键菜单已由 DockWindow8 自治挂接
|
||||
// (操作对象为当前打开的方案文件,不再是 fak.db)----'''
|
||||
restore = ''' // ---- 方案窗口 三表右键菜单(操作当前打开的方案文件)----
|
||||
_menuFakTuhao = new ContextMenuStrip();
|
||||
_menuFakTuhao.Items.Add("删除图号", null, FakTuhao_Delete_Click);
|
||||
_menuFakTuhao.Items.Add(MakeUndoMenuItem());
|
||||
_menuFakTuhao.Items.Add(new ToolStripSeparator());
|
||||
_menuFakTuhao.Items.Add("插入图号到当前项目", null, FakTuhao_Insert_Click);
|
||||
this.advancedDataGridView图号.ContextMenuStrip = _menuFakTuhao;
|
||||
Log(" 方案图号菜单已挂到 advancedDataGridView图号(删除/插入到当前项目)");
|
||||
|
||||
_menuFakXianghao = new ContextMenuStrip();
|
||||
_menuFakXianghao.Items.Add("删除箱号", null, FakXianghao_Delete_Click);
|
||||
_menuFakXianghao.Items.Add(MakeUndoMenuItem());
|
||||
_menuFakXianghao.Items.Add(new ToolStripSeparator());
|
||||
_menuFakXianghao.Items.Add("插入箱号到当前项目", null, FakXianghao_Insert_Click);
|
||||
this.advancedDataGridView箱号.ContextMenuStrip = _menuFakXianghao;
|
||||
Log(" 方案箱号菜单已挂到 advancedDataGridView箱号(删除/插入到当前项目)");
|
||||
|
||||
_menuFakYuanjian = new ContextMenuStrip();
|
||||
_menuFakYuanjian.Items.Add("删除元件", null, FakYuanjian_Delete_Click);
|
||||
_menuFakYuanjian.Items.Add(MakeUndoMenuItem());
|
||||
_menuFakYuanjian.Items.Add(new ToolStripSeparator());
|
||||
_menuFakYuanjian.Items.Add("插入元件到当前项目", null, FakYuanjian_Insert_Click);
|
||||
this.advancedDataGridView元件.ContextMenuStrip = _menuFakYuanjian;
|
||||
Log(" 方案元件菜单已挂到 advancedDataGridView元件(删除/插入到当前项目)");
|
||||
|
||||
// ---- 方案窗口(advancedDataGridView图号/箱号/元件)右键菜单已由 DockWindow8 自治挂接
|
||||
// (操作对象为当前打开的方案文件,不再是 fak.db)----'''
|
||||
if anchor2 in s:
|
||||
s = s.replace(anchor2, restore, 1)
|
||||
n += 1
|
||||
print("菜单挂接已恢复")
|
||||
else:
|
||||
print("警告: 未找到方案窗口右键挂接注释锚点(可能此前未删除)")
|
||||
# 恢复挂接直接追加在 元件菜单挂接 之后
|
||||
anchor3 = 'Log(" 元件菜单已挂到 advancedDataGridView5(含「加入用户库」「加入柜体库」+ 行移动)");'
|
||||
if anchor3 in s and "_menuFakTuhao = new ContextMenuStrip();" not in s:
|
||||
s = s.replace(anchor3, anchor3 + "\n" + restore.split("\n", 2)[2], 1)
|
||||
n += 1
|
||||
print("挂接已追加到元件菜单后")
|
||||
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("合计", n, "处")
|
||||
@@ -0,0 +1,55 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""①全部 Fak* handler 的数据源 fak.db → 方案窗口当前打开的方案文件(统一语义)
|
||||
②ReloadFakData 改为通知方案窗口重新加载当前方案
|
||||
③DockWindow8 加 重新加载当前方案() 方法。幂等。"""
|
||||
import io
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\主窗口.菜单与库.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
n = 0
|
||||
|
||||
# 1) 路径定义统一替换(handler 数据源 → 当前方案文件)
|
||||
pairs = [
|
||||
('string fakDbPath = GetDbFullPath(@"sjk\\fak.db");', 'string fakDbPath = 方案数据源路径();'),
|
||||
('string srcDbPath = GetDbFullPath(@"sjk\\fak.db");', 'string srcDbPath = 方案数据源路径();'),
|
||||
('"方案库不存在:\\n" + fakDbPath', '"请先在方案窗口项目页双击打开一个方案"'),
|
||||
('"方案库不存在:\\n" + srcDbPath', '"请先在方案窗口项目页双击打开一个方案"'),
|
||||
]
|
||||
for old, new in pairs:
|
||||
c = s.count(old)
|
||||
s = s.replace(old, new)
|
||||
n += c
|
||||
print("替换", c, "处:", old[:50])
|
||||
|
||||
# 2) ReloadFakData 改为通知方案窗口重载当前方案
|
||||
old_rf = ''' public void ReloadFakData()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.w8 != null) this.w8.刷新项目页();
|
||||
Log("[方案刷新] 已通知方案窗口刷新方案列表");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log("[方案刷新] 失败: " + ex.Message);
|
||||
}
|
||||
}'''
|
||||
new_rf = ''' public void ReloadFakData()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (this.w8 != null) this.w8.重新加载当前方案();
|
||||
Log("[方案刷新] 已通知方案窗口重载当前方案");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log("[方案刷新] 失败: " + ex.Message);
|
||||
}
|
||||
}'''
|
||||
if old_rf in s:
|
||||
s = s.replace(old_rf, new_rf, 1)
|
||||
n += 1
|
||||
print("ReloadFakData 已改")
|
||||
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("合计", n, "处")
|
||||
@@ -0,0 +1,24 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""修 paste_optimize.py 中图号行触发器的锚点(适配现签名),并加开关条件。"""
|
||||
import io
|
||||
|
||||
p = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\tools\paste_optimize.py"
|
||||
s = io.open(p, encoding="utf-8").read()
|
||||
|
||||
old = (
|
||||
'\' "_u AFTER UPDATE OF \\\\"" + 图号输入列[0] + "\\\\"" ON \\\\"" + 图号表名 + "\\\\"" FOR EACH ROW WHEN NEW.\\\\\"内部ID\\\\\"=\'" +\n'
|
||||
"\t\t\t\t图号ID.Replace(\"'\", \"''\") + \"' BEGIN UPDATE \\\\\"\" + 图号表名 + \"\\\\\" SET \" + thSetNew +"
|
||||
)
|
||||
# 直接用朴素文本定位(避免转义嵌套):找 'BEGIN UPDATE' 紧邻 thSetNew 的那一行对
|
||||
lines = s.split("\n")
|
||||
done = False
|
||||
for i, l in enumerate(lines):
|
||||
if "FOR EACH ROW WHEN NEW." in l and "图号输入列[0]" in l and i + 2 < len(lines):
|
||||
if "' BEGIN UPDATE" in lines[i + 2] and "thSetNew" in lines[i + 2]:
|
||||
lines[i + 2] = lines[i + 2].replace("' BEGIN UPDATE", "' AND \" + 开关条件 + \" BEGIN UPDATE", 1)
|
||||
done = True
|
||||
print("锚点行已改:", lines[i + 2].strip()[:80])
|
||||
break
|
||||
if done:
|
||||
io.open(p, "w", encoding="utf-8").write("\n".join(lines))
|
||||
print("done", done)
|
||||
@@ -0,0 +1,206 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
把旧工具的 Columns.Add + Columns[i].X 写法转换为 SharpDevelop 设计器认的
|
||||
"列字段 + AddRange" 标准 VS 写法,让设计器里能显示列头。
|
||||
"""
|
||||
import io
|
||||
import re
|
||||
import os
|
||||
import glob
|
||||
|
||||
BASE = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
|
||||
|
||||
|
||||
def convert_columns(designer_text, class_name):
|
||||
"""把一个 Designer.cs 里的所有 DataGridView 列定义从旧格式转换为 VS 标准格式"""
|
||||
lines = designer_text.split("\n")
|
||||
out = []
|
||||
all_column_fields = [] # 所有列字段声明
|
||||
all_column_news = [] # 所有列 new 语句
|
||||
all_column_blocks = [] # 所有列属性块
|
||||
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i].strip()
|
||||
|
||||
# 检测: this.xxx.Columns.Add(new System.Windows.Forms.DataGridViewTextBoxColumn());
|
||||
m_add = re.match(r'this\.(\w+)\.Columns\.Add\(new System\.Windows\.Forms\.(\w+Column)\(\)\);', line)
|
||||
if not m_add:
|
||||
out.append(lines[i])
|
||||
i += 1
|
||||
continue
|
||||
|
||||
grid_name = m_add.group(1)
|
||||
col_type = m_add.group(2)
|
||||
|
||||
# 收集后续的 Columns[i].X = ... 行
|
||||
col_idx = 0
|
||||
col_props = []
|
||||
j = i + 1
|
||||
while j < len(lines):
|
||||
st = lines[j].strip()
|
||||
m_prop = re.match(r'this\.' + grid_name + r'\.Columns\[(\d+)\]\.(\w+(?:\.\w+)*)\s*=\s*(.+?);', st)
|
||||
if not m_prop:
|
||||
break
|
||||
idx = int(m_prop.group(1))
|
||||
if idx != col_idx:
|
||||
break
|
||||
col_props.append((m_prop.group(2), m_prop.group(3)))
|
||||
j += 1
|
||||
|
||||
if not col_props:
|
||||
out.append(lines[i])
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# 生成 VS 标准写法
|
||||
# 列名从 HeaderText 取
|
||||
header = ""
|
||||
for prop, val in col_props:
|
||||
if prop == "HeaderText":
|
||||
header = val.strip('"')
|
||||
break
|
||||
field_name = "dataGridViewColumn_%s_%d" % (grid_name, col_idx)
|
||||
|
||||
# 收集属性
|
||||
prop_lines = []
|
||||
for prop, val in col_props:
|
||||
if prop == "Name" and val == '""':
|
||||
continue # 跳过空 Name
|
||||
prop_lines.append("\t\t\tthis.%s.%s = %s;" % (field_name, prop, val))
|
||||
|
||||
all_column_news.append("\t\t\tthis.%s = new System.Windows.Forms.%s();" % (field_name, col_type))
|
||||
all_column_blocks.append("\t\t\t// \n\t\t\t// %s\n\t\t\t// " % field_name)
|
||||
all_column_blocks.extend(prop_lines)
|
||||
all_column_fields.append("\t\tprivate System.Windows.Forms.%s %s;" % (col_type, field_name))
|
||||
|
||||
# 记住这个列属于哪个 grid
|
||||
if not hasattr(convert_columns, "_grid_cols"):
|
||||
convert_columns._grid_cols = {}
|
||||
convert_columns._grid_cols.setdefault(grid_name, []).append(field_name)
|
||||
|
||||
col_idx += 1
|
||||
# 继续找下一个 Columns.Add(同一 grid 的下一列)
|
||||
while j < len(lines):
|
||||
st2 = lines[j].strip()
|
||||
m_next = re.match(r'this\.' + grid_name + r'\.Columns\.Add\(new System\.Windows\.Forms\.(\w+Column)\(\)\);', st2)
|
||||
if m_next:
|
||||
col_type2 = m_next.group(1)
|
||||
j += 1
|
||||
col_props2 = []
|
||||
while j < len(lines):
|
||||
st3 = lines[j].strip()
|
||||
m_prop2 = re.match(r'this\.' + grid_name + r'\.Columns\[(\d+)\]\.(\w+(?:\.\w+)*)\s*=\s*(.+?);', st3)
|
||||
if not m_prop2:
|
||||
break
|
||||
col_props2.append((m_prop2.group(2), m_prop2.group(3)))
|
||||
j += 1
|
||||
|
||||
field_name2 = "dataGridViewColumn_%s_%d" % (grid_name, col_idx)
|
||||
for prop, val in col_props2:
|
||||
if prop == "HeaderText":
|
||||
header2 = val.strip('"')
|
||||
break
|
||||
prop_lines2 = []
|
||||
for prop, val in col_props2:
|
||||
if prop == "Name" and val == '""':
|
||||
continue
|
||||
prop_lines2.append("\t\t\tthis.%s.%s = %s;" % (field_name2, prop, val))
|
||||
all_column_news.append("\t\t\tthis.%s = new System.Windows.Forms.%s();" % (field_name2, col_type2))
|
||||
all_column_blocks.append("\t\t\t// \n\t\t\t// %s\n\t\t\t// " % field_name2)
|
||||
all_column_blocks.extend(prop_lines2)
|
||||
all_column_fields.append("\t\tprivate System.Windows.Forms.%s %s;" % (col_type2, field_name2))
|
||||
convert_columns._grid_cols.setdefault(grid_name, []).append(field_name2)
|
||||
col_idx += 1
|
||||
else:
|
||||
break
|
||||
|
||||
# 跳过所有已消费的行
|
||||
i = j
|
||||
|
||||
# 在 SuspendLayout 之前插入列的 new 语句
|
||||
result = []
|
||||
inserted_news = False
|
||||
for line in out:
|
||||
if not inserted_news and "this.SuspendLayout()" in line:
|
||||
result.extend(all_column_news)
|
||||
inserted_news = True
|
||||
result.append(line)
|
||||
|
||||
# 在最后一个控件属性块之后、Controls.Add 之前插入列属性块和 AddRange
|
||||
final = []
|
||||
inserted_blocks = False
|
||||
for k, line in enumerate(result):
|
||||
# 找 Controls.Add 或 AddRange(第一个容器层级的地方)
|
||||
if not inserted_blocks and re.search(r'this\.\w+\.Controls\.Add', line.strip()):
|
||||
# 插入列属性块
|
||||
final.extend(all_column_blocks)
|
||||
# 插入每个 grid 的 Columns.AddRange
|
||||
for grid_name, fields in convert_columns._grid_cols.items():
|
||||
inner = ",\n\t\t\t\t".join("this.%s" % f for f in fields)
|
||||
final.append("\t\t\tthis.%s.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {" % grid_name)
|
||||
final.append("\t\t\t\t" + inner + "});")
|
||||
final.append("")
|
||||
inserted_blocks = True
|
||||
final.append(line)
|
||||
|
||||
if not inserted_blocks:
|
||||
# 没找到 Controls.Add,直接在 ResumeLayout 前插
|
||||
final2 = []
|
||||
for line in final:
|
||||
if "this.ResumeLayout" in line and not inserted_blocks:
|
||||
final2.extend(all_column_blocks)
|
||||
for grid_name, fields in convert_columns._grid_cols.items():
|
||||
inner = ",\n\t\t\t\t".join("this.%s" % f for f in fields)
|
||||
final2.append("\t\t\tthis.%s.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {" % grid_name)
|
||||
final2.append("\t\t\t\t" + inner + "});")
|
||||
final2.append("")
|
||||
inserted_blocks = True
|
||||
final2.append(line)
|
||||
final = final2
|
||||
|
||||
# 在最后一个 private 字段声明之前插入列字段
|
||||
final3 = []
|
||||
inserted_fields = False
|
||||
for line in final:
|
||||
if not inserted_fields and re.match(r'\t\tprivate ', line) and "Column" not in line:
|
||||
final3.extend(all_column_fields)
|
||||
final3.append("")
|
||||
inserted_fields = True
|
||||
final3.append(line)
|
||||
|
||||
if not inserted_fields:
|
||||
final3.extend(all_column_fields)
|
||||
|
||||
# 清理类级变量
|
||||
convert_columns._grid_cols = {}
|
||||
|
||||
return "\n".join(final3), len(all_column_fields)
|
||||
|
||||
|
||||
def process_file(path):
|
||||
s = io.open(path, encoding="utf-8-sig").read()
|
||||
# 提取类名
|
||||
m = re.search(r'partial class (\w+)', s)
|
||||
if not m:
|
||||
return 0
|
||||
class_name = m.group(1)
|
||||
|
||||
# 检查是否有旧格式的列定义
|
||||
if "Columns.Add(new" not in s:
|
||||
return 0
|
||||
|
||||
new_s, n_cols = convert_columns(s, class_name)
|
||||
if n_cols > 0:
|
||||
io.open(path, "w", encoding="utf-8-sig", newline="\r\n").write(new_s)
|
||||
print("%s: 转换了 %d 个列字段" % (os.path.basename(path), n_cols))
|
||||
return n_cols
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
total = 0
|
||||
for pattern in ["xiangmu/*.Designer.cs", "shezhi/*.Designer.cs",
|
||||
"ku/*.Designer.cs", "GCS母排计算/*.Designer.cs"]:
|
||||
for f in glob.glob(os.path.join(BASE, pattern)):
|
||||
total += process_file(f)
|
||||
print("\n共转换 %d 个列" % total)
|
||||
@@ -0,0 +1,12 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""把 csproj 里 Smart.CustomComponents 的 HintPath 从安装目录改指仓库 bin(最新构建)。"""
|
||||
import io
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\成套报价软件.csproj"
|
||||
OLD = r"<HintPath>..\..\..\..\..\..\Program Files %28x86%29\SharpDevelop\5.2\bin\Smart.CustomComponents.dll</HintPath>"
|
||||
NEW = r"<HintPath>F:\Visual Studio\1\33\SharpDevelop-master\bin\Smart.CustomComponents.dll</HintPath>"
|
||||
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
assert OLD in s, "旧 HintPath 未找到"
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s.replace(OLD, NEW))
|
||||
print("HintPath 已指向仓库 bin(最新构建)")
|
||||
@@ -0,0 +1,117 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""项目右键"加入方案库"插入段修正版:在 复制图号到方案库 方法前插入 项目右键处理方法。幂等。"""
|
||||
import io
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\主窗口.菜单与库.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
|
||||
anchor = " /// <summary>复制一个图号(含箱号/元件子树)从项目库到方案库。供单图号加入与整项目加入共用。</summary>"
|
||||
assert anchor in s, "插入锚缺失"
|
||||
|
||||
if "MenuProject_AddToFak_Click" in s:
|
||||
print("已存在,跳过")
|
||||
else:
|
||||
proj_click = ''' /// <summary>项目表右键「加入方案库」:把整个项目(全部图号+箱号+元件+项目信息)复制到方案库。</summary>
|
||||
private void MenuProject_AddToFak_Click(object sender, EventArgs e)
|
||||
{
|
||||
var row = GetFirstSelectedRow(this.advancedDataGridView6);
|
||||
if (row == null)
|
||||
{
|
||||
MessageBox.Show(this, "请先在项目列表中选择要加入方案库的项目。",
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
string projectId = GetBoundValueAsString(row, "项目编号");
|
||||
if (string.IsNullOrWhiteSpace(projectId))
|
||||
{
|
||||
MessageBox.Show(this, "选中行没有项目编号,无法加入方案库。",
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
string srcDbPath = GetQuoteDbPath(projectId);
|
||||
if (!System.IO.File.Exists(srcDbPath))
|
||||
{
|
||||
MessageBox.Show(this, "项目报价库不存在:\\n" + srcDbPath,
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
string fakDbPath = GetDbFullPath(@"sjk\\fak.db");
|
||||
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
Cursor = Cursors.WaitCursor;
|
||||
try
|
||||
{
|
||||
var 图号们 = new System.Collections.Generic.List<string>();
|
||||
using (var conn = new System.Data.SQLite.SQLiteConnection(GetConnString(srcDbPath)))
|
||||
{
|
||||
conn.Open();
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT 内部ID FROM " + TuhaoTableName + " ORDER BY CAST(序号 AS INTEGER);", conn))
|
||||
using (var r = cmd.ExecuteReader())
|
||||
while (r.Read())
|
||||
{
|
||||
if (!r.IsDBNull(0))
|
||||
{
|
||||
string v = r.GetString(0);
|
||||
if (!string.IsNullOrEmpty(v)) 图号们.Add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (图号们.Count == 0)
|
||||
{
|
||||
MessageBox.Show(this, "项目 [" + projectId + "] 没有图号数据,无法加入方案库。",
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var thId in 图号们)
|
||||
复制图号到方案库(projectId, thId, srcDbPath, fakDbPath);
|
||||
|
||||
string projTbl = ProjectInfoTableName;
|
||||
using (var srcConn = new System.Data.SQLite.SQLiteConnection(GetConnString(srcDbPath)))
|
||||
using (var dstConn = new System.Data.SQLite.SQLiteConnection(GetConnString(fakDbPath)))
|
||||
{
|
||||
srcConn.Open();
|
||||
dstConn.Open();
|
||||
var infoRow = new DataTable();
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT * FROM " + projTbl + " WHERE 项目编号=@id LIMIT 1;", srcConn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@id", projectId);
|
||||
using (var ad = new System.Data.SQLite.SQLiteDataAdapter(cmd)) ad.Fill(infoRow);
|
||||
}
|
||||
var cols = new System.Collections.Generic.List<string>();
|
||||
foreach (DataColumn c in infoRow.Columns) cols.Add(c.ColumnName);
|
||||
using (var tx = dstConn.BeginTransaction())
|
||||
{
|
||||
Exec(dstConn, tx, "DELETE FROM " + projTbl + ";");
|
||||
foreach (DataRow r in infoRow.Rows)
|
||||
InsertRowByColumns(r, projTbl, cols, dstConn, tx);
|
||||
tx.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
Log("[加入方案库] 整项目完成: 项目=" + projectId + " 图号=" + 图号们.Count + " 耗时 " + sw.ElapsedMilliseconds + "ms");
|
||||
ReloadFakData();
|
||||
MessageBox.Show(this, "已把项目 [" + projectId + "] 加入方案库。\\n共 " + 图号们.Count + " 个图号,耗时 " +
|
||||
sw.ElapsedMilliseconds + "ms。", "加入方案库",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
catch (Exception exAdd)
|
||||
{
|
||||
Log("[加入方案库] 整项目失败: " + exAdd);
|
||||
MessageBox.Show(this, "加入方案库失败:\\n" + exAdd.Message, "加入方案库",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cursor = Cursors.Default;
|
||||
}
|
||||
}
|
||||
|
||||
'''
|
||||
s = s.replace(anchor, proj_click + anchor, 1)
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("项目右键方法已插入")
|
||||
@@ -0,0 +1,20 @@
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$src = 'C:\Users\099978\Documents\SharpDevelop Projects\chengtao\pdfbt\libs\AdvancedDataGridView.dll'
|
||||
$dst = 'C:\Program Files (x86)\SharpDevelop\5.2\bin\AdvancedDataGridView.dll'
|
||||
if (Test-Path $dst) { Write-Output 'exists-skip'; exit 0 }
|
||||
$dstDir = 'C:\Program Files (x86)\SharpDevelop\5.2\bin'
|
||||
# 中文路径占位替换(写文件时避免编码问题): 用真实路径覆盖
|
||||
$src = $src -replace 'chengtao', [char]0x6210 + [char]0x5957 + [char]0x62A5 + [char]0x4EF7
|
||||
try {
|
||||
Copy-Item -LiteralPath $src -Destination $dst -ErrorAction Stop
|
||||
Write-Output 'copy-ok'
|
||||
exit 0
|
||||
} catch { Write-Output ('copy-fail: ' + $_.Exception.Message) }
|
||||
try {
|
||||
$me = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
||||
icacls $dstDir /grant "${me}:(OI)(CI)M" | Out-Null
|
||||
Copy-Item -LiteralPath $src -Destination $dst -ErrorAction Stop
|
||||
Write-Output 'icacls-ok'
|
||||
exit 0
|
||||
} catch { Write-Output ('icacls-fail: ' + $_.Exception.Message) }
|
||||
Write-Output 'ALL_FAILED'
|
||||
@@ -0,0 +1,87 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
成套报价软件图标生成器 —— 与 app.svg 设计稿完全等价的 Pillow 绘制。
|
||||
纯 Python + Pillow,零原生依赖(本机没有 cairo,SVG 无法直接栅格化)。
|
||||
设计:深蓝→青蓝渐变圆角底 + 白色开关柜轮廓 + 三根铜排 + 绿色 ¥ 角标。
|
||||
512 高分辨率绘制后缩放出多尺寸 app.ico,保证 16px 也清晰。
|
||||
"""
|
||||
import os
|
||||
import math
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
OUT_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "成套报价软件"))
|
||||
ICO_PATH = os.path.join(OUT_DIR, "app.ico")
|
||||
PNG_PATH = os.path.join(OUT_DIR, "app_512.png")
|
||||
|
||||
SIZES = [16, 32, 48, 64, 128, 256]
|
||||
RENDER = 512 # 与 SVG viewBox 一致,坐标 1:1
|
||||
|
||||
|
||||
def lerp(c1, c2, t):
|
||||
return (int(c1[0] + (c2[0] - c1[0]) * t),
|
||||
int(c1[1] + (c2[1] - c1[1]) * t),
|
||||
int(c1[2] + (c2[2] - c1[2]) * t))
|
||||
|
||||
|
||||
def rounded_gradient_rect(img, box, c_top, c_bot, radius):
|
||||
"""垂直渐变圆角矩形(逐行渐变 + 圆角 mask)。"""
|
||||
x0, y0, x1, y1 = box
|
||||
w, h = x1 - x0, y1 - y0
|
||||
grad = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
||||
gp = grad.load()
|
||||
for y in range(h):
|
||||
c = lerp(c_top, c_bot, y / max(h - 1, 1))
|
||||
for x in range(w):
|
||||
gp[x, y] = (c[0], c[1], c[2], 255)
|
||||
mask = Image.new("L", (w, h), 0)
|
||||
ImageDraw.Draw(mask).rounded_rectangle([0, 0, w - 1, h - 1], radius=radius, fill=255)
|
||||
img.paste(grad, (x0, y0), mask)
|
||||
|
||||
|
||||
def line_round(draw, p1, p2, width, fill):
|
||||
"""带圆头的粗线。"""
|
||||
draw.line([p1, p2], fill=fill, width=width)
|
||||
r = width / 2.0
|
||||
for p in (p1, p2):
|
||||
draw.ellipse([p[0] - r, p[1] - r, p[0] + r, p[1] + r], fill=fill)
|
||||
|
||||
|
||||
def main():
|
||||
img = Image.new("RGBA", (RENDER, RENDER), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
# 1) 渐变圆角背景 (16,16)-(496,496) r=100,#1d4ed8→#0e7490
|
||||
rounded_gradient_rect(img, (16, 16, 496, 496), (29, 78, 216), (14, 116, 144), 100)
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
# 2) 开关柜轮廓 (118,88)-(394,392) r=30 白色描边 24
|
||||
d.rounded_rectangle([118, 88, 394, 392], radius=30, outline=(255, 255, 255, 255), width=24)
|
||||
|
||||
# 3) 三根铜排 (160..352),高 34,r=9,铜色渐变近似为纯色+顶部亮色高光线
|
||||
for y in (138, 218, 298):
|
||||
d.rounded_rectangle([160, y, 352, y + 34], radius=9, fill=(234, 141, 25, 255)) # #ea8d19 主体
|
||||
d.rounded_rectangle([160, y, 352, y + 14], radius=7, fill=(251, 191, 36, 255)) # #fbbf24 顶部亮
|
||||
|
||||
# 4) ¥ 角标:绿圆 (376,376) r=94 + 白描边 14
|
||||
d.ellipse([376 - 94, 376 - 94, 376 + 94, 376 + 94],
|
||||
fill=(16, 185, 129, 255), outline=(255, 255, 255, 255), width=14)
|
||||
|
||||
# ¥ 笔画(白,线宽 19,圆头)
|
||||
W = (255, 255, 255, 255)
|
||||
lw = 19
|
||||
line_round(d, (338, 338), (378, 380), lw, W) # 撇
|
||||
line_round(d, (416, 338), (376, 380), lw, W) # 捺
|
||||
line_round(d, (340, 392), (412, 392), lw, W) # 上横
|
||||
line_round(d, (340, 420), (412, 420), lw, W) # 下横
|
||||
line_round(d, (377, 414), (377, 442), lw, W) # 竖
|
||||
|
||||
# 5) 导出:512 PNG 预览 + 多尺寸 ICO
|
||||
img.save(PNG_PATH)
|
||||
img.save(ICO_PATH, sizes=[(s, s) for s in SIZES])
|
||||
print("已生成:", ICO_PATH)
|
||||
print("已生成:", PNG_PATH)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""组装发布目录 release\成套报价软件\(exe + 依赖 DLL + 图标 + 运行数据目录)。"""
|
||||
import os
|
||||
import shutil
|
||||
|
||||
ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
||||
BIN = os.path.join(ROOT, "成套报价软件", "bin", "Debug")
|
||||
REL = os.path.join(ROOT, "release", "成套报价软件")
|
||||
|
||||
DLLS = [
|
||||
"AdvancedDataGridView.dll", "Smart.CustomComponents.dll",
|
||||
"WeifenLuo.WinFormsUI.Docking.dll", "WeifenLuo.WinFormsUI.Docking.ThemeVS2015.dll",
|
||||
"System.Data.SQLite.dll", "System.Resources.Extensions.dll", "System.Memory.dll",
|
||||
"System.Numerics.Vectors.dll", "System.Runtime.CompilerServices.Unsafe.dll",
|
||||
"Microsoft.Web.WebView2.Core.dll", "Microsoft.Web.WebView2.WinForms.dll", "WebView2Loader.dll",
|
||||
"pdfium.dll",
|
||||
"NPOI.dll", "NPOI.OOXML.dll", "NPOI.OpenXml4Net.dll", "NPOI.OpenXmlFormats.dll",
|
||||
"DotNetZip.dll", "ICSharpCode.SharpZipLib.dll", "unvell.ReoGrid.dll", "CJZZ.Controls.dll",
|
||||
]
|
||||
DATA_DIRS = ["sjk", "GCS铜排壳体计算", "shezhi", "xiangmu"]
|
||||
|
||||
|
||||
def main():
|
||||
# 只清掉旧的同名文件,保留 setup.iss 和 Output 目录(安装脚本手工维护)
|
||||
os.makedirs(REL, exist_ok=True)
|
||||
for name in os.listdir(REL):
|
||||
p = os.path.join(REL, name)
|
||||
if name in ("setup.iss", "Output"):
|
||||
continue
|
||||
if os.path.isdir(p):
|
||||
shutil.rmtree(p)
|
||||
else:
|
||||
os.remove(p)
|
||||
|
||||
# ★ WebView2 运行时引导程序(setup.iss [Files] 引用;新装机缺运行时时安装期静默补装)
|
||||
wv2 = os.path.join(ROOT, "tools", "MicrosoftEdgeWebview2Setup.exe")
|
||||
if os.path.exists(wv2):
|
||||
shutil.copy2(wv2, os.path.join(REL, "MicrosoftEdgeWebview2Setup.exe"))
|
||||
print("复制 MicrosoftEdgeWebview2Setup.exe(WebView2 运行时引导程序)")
|
||||
else:
|
||||
print("!! 缺少 tools/MicrosoftEdgeWebview2Setup.exe(新装机缺 WebView2 时无法自动补装)")
|
||||
|
||||
# exe + 配置 + 图标
|
||||
for f in ["成套报价软件.exe", "成套报价软件.exe.config", "app.ico"]:
|
||||
src = os.path.join(BIN, f)
|
||||
if not os.path.exists(src):
|
||||
src = os.path.join(ROOT, "成套报价软件", f) # app.ico 在项目根
|
||||
shutil.copy2(src, REL)
|
||||
print("复制", f)
|
||||
|
||||
# 依赖 DLL
|
||||
missing = []
|
||||
for dll in DLLS:
|
||||
src = os.path.join(BIN, dll)
|
||||
if os.path.exists(src):
|
||||
shutil.copy2(src, REL)
|
||||
else:
|
||||
missing.append(dll)
|
||||
print("DLL 复制完成,缺失:", missing if missing else "无")
|
||||
|
||||
# 功能区按钮图标目录(exe 旁 icons\,运行时按绝对路径加载)
|
||||
icons_src = os.path.join(BIN, "icons")
|
||||
if os.path.isdir(icons_src):
|
||||
shutil.copytree(icons_src, os.path.join(REL, "icons"))
|
||||
print("复制目录 icons(功能区按钮图标 %d 个)" % len(os.listdir(icons_src)))
|
||||
|
||||
# 开源许可证全文目录(exe 旁 licenses\,关于窗口「查看许可证全文」用)
|
||||
lic_src = os.path.join(BIN, "licenses")
|
||||
if os.path.isdir(lic_src):
|
||||
shutil.copytree(lic_src, os.path.join(REL, "licenses"))
|
||||
print("复制目录 licenses(开源许可证全文 %d 份)" % len(os.listdir(lic_src)))
|
||||
|
||||
# 元件库网页(node 静态服务:html+server.cjs+jquery;2026-09-13 页面已改公网 dx24test.dhub.com.cn,
|
||||
# 本地 data 数据树/access.log/.mimosa/旧数据包 zip 一律不进包)
|
||||
yj_src = os.path.join(BIN, "元件库网页")
|
||||
if os.path.isdir(yj_src):
|
||||
shutil.copytree(yj_src, os.path.join(REL, "元件库网页"),
|
||||
ignore=shutil.ignore_patterns("__pycache__", "data", "access.log", ".mimosa", "*.zip"))
|
||||
print("复制目录 元件库网页(%d 文件)" % sum(len(fs) for _, _, fs in os.walk(yj_src)))
|
||||
else:
|
||||
print("!! 缺少目录 元件库网页")
|
||||
|
||||
# 运行数据目录
|
||||
for d in DATA_DIRS:
|
||||
src = os.path.join(BIN, d)
|
||||
if not os.path.isdir(src):
|
||||
print("!! 缺少数据目录", d)
|
||||
continue
|
||||
dst = os.path.join(REL, d)
|
||||
if d == "shezhi":
|
||||
# 只带铜排/铝排参数;企业信息、收藏、最近项目等用户私有配置不带
|
||||
os.makedirs(dst)
|
||||
for f in ("铜排参数.config.xml", "铝排参数.config.xml"):
|
||||
fp = os.path.join(src, f)
|
||||
if os.path.exists(fp):
|
||||
shutil.copy2(fp, dst)
|
||||
print("复制 shezhi/" + f)
|
||||
elif d == "sjk":
|
||||
# 库数据照拷;排除项目列表库(程序自动建)和 bjb 各项目报价库(只留空目录)
|
||||
shutil.copytree(src, dst, ignore=shutil.ignore_patterns("advancedDataGridView6Table.db"))
|
||||
bjb = os.path.join(dst, "bjb")
|
||||
if os.path.isdir(bjb):
|
||||
shutil.rmtree(bjb)
|
||||
os.makedirs(bjb)
|
||||
# 方案库是用户私有方案文件(同 bjb 策略留空,程序自动建)
|
||||
fangan = os.path.join(dst, "方案库")
|
||||
if os.path.isdir(fangan):
|
||||
shutil.rmtree(fangan)
|
||||
os.makedirs(fangan)
|
||||
print("复制目录 sjk(排除项目库,bjb/方案库 留空)")
|
||||
else:
|
||||
# GCS铜排壳体计算:只带运行文件,排除遗留源码/构建脚本(2026-09-06 用户要求源码不进包)
|
||||
if d == "GCS铜排壳体计算":
|
||||
shutil.copytree(src, dst,
|
||||
ignore=shutil.ignore_patterns("*.cs", "*.xml", "*.bat", "*.rsp", "*.py", "build*"))
|
||||
print("复制目录 GCS铜排壳体计算(排除源码/构建脚本)")
|
||||
continue
|
||||
shutil.copytree(src, dst,
|
||||
ignore=shutil.ignore_patterns("__pycache__", "*.pyc"))
|
||||
print("复制目录", d)
|
||||
|
||||
# ===== cadbt(CAD扒图独立程序,默认安装) =====
|
||||
cadbt_src = os.path.join(ROOT, "cadbt", "cadbt", "bin", "Debug")
|
||||
cadbt_dst = os.path.join(REL, "cadbt")
|
||||
os.makedirs(cadbt_dst, exist_ok=True)
|
||||
for f in os.listdir(cadbt_src):
|
||||
if f.endswith((".exe", ".dll", ".config")):
|
||||
shutil.copy2(os.path.join(cadbt_src, f), os.path.join(cadbt_dst, f))
|
||||
os.makedirs(os.path.join(cadbt_dst, "node"), exist_ok=True)
|
||||
shutil.copy2(os.path.join(cadbt_src, "node", "node.exe"), os.path.join(cadbt_dst, "node", "node.exe"))
|
||||
dist_src = os.path.join(ROOT, "cadbt", "cadbt", "CAD-Viewer", "packages", "cad-viewer-example", "dist")
|
||||
dist_dst = os.path.join(cadbt_dst, "CAD-Viewer", "dist")
|
||||
shutil.copytree(dist_src, dist_dst)
|
||||
# ★ vite build 只产出 assets/index.html/testdata;fonts(中文字体库)/templates/
|
||||
# server.cjs 来自原厂预编译包,源码树没有——从部署机运行目录补齐
|
||||
# (2026-09-20 补:此前安装包漏带这三样,装机机 CAD 文字渲染缺字体)
|
||||
runtime_dist = r"C:\成套报价软件\cadbt\CAD-Viewer\dist"
|
||||
for extra in ("fonts", "templates"):
|
||||
p = os.path.join(runtime_dist, extra)
|
||||
if os.path.isdir(p):
|
||||
shutil.copytree(p, os.path.join(dist_dst, extra))
|
||||
print("补齐 dist/%s(%d 文件)" % (extra, sum(len(fs) for _, _, fs in os.walk(p))))
|
||||
else:
|
||||
print("!! 运行目录缺少", extra, "(安装机 CAD 文字渲染会缺字体!)")
|
||||
sc = os.path.join(runtime_dist, "server.cjs")
|
||||
if os.path.exists(sc):
|
||||
shutil.copy2(sc, os.path.join(dist_dst, "server.cjs"))
|
||||
print("补齐 dist/server.cjs")
|
||||
else:
|
||||
print("!! 运行目录缺少 server.cjs(CAD 网页服务起不来!)")
|
||||
os.makedirs(os.path.join(cadbt_dst, "sjk", "bjb"), exist_ok=True)
|
||||
print("复制目录 cadbt(exe+DLL+node+dist 网页+fonts/templates/server.cjs)")
|
||||
|
||||
# ===== pdfbt(PDF扒图独立程序+OCR Python 环境,默认安装) =====
|
||||
pdfbt_src = os.path.join(ROOT, "pdfbt", "bin", "Debug")
|
||||
pdfbt_dst = os.path.join(REL, "pdfbt")
|
||||
os.makedirs(pdfbt_dst, exist_ok=True)
|
||||
for f in os.listdir(pdfbt_src):
|
||||
if f.endswith((".exe", ".dll", ".config")):
|
||||
shutil.copy2(os.path.join(pdfbt_src, f), os.path.join(pdfbt_dst, f))
|
||||
os.makedirs(os.path.join(pdfbt_dst, "sjk", "bjb"), exist_ok=True)
|
||||
print("复制目录 pdfbt(exe+pdfium,OCR=百度API无需本地环境)")
|
||||
|
||||
# ===== AI 助手引擎目录(2026-09-26 OpenClaw→ZCode 开源版 zai-org/ZCode) =====
|
||||
# ai助手\ = zcode\(官方 build:zcode 发行包:bin/web/server/agent/node_modules + 捆绑 node)
|
||||
# + zcode预置\(技能/provider模板/密钥/版本戳) + 启停 cmd,位于仓库根。
|
||||
# 排除运行时产物:zcode数据\(装机从零初始化;ZCODE_DATA_BASE_DIR 隔离目录)与日志。
|
||||
ai_src = os.path.join(ROOT, "ai助手")
|
||||
ai_dst = os.path.join(REL, "ai助手")
|
||||
if os.path.isdir(ai_src):
|
||||
shutil.copytree(
|
||||
ai_src, ai_dst,
|
||||
# openclaw=旧引擎空壳目录(进程句柄占着删不掉,重启自清;零文件无实害,不进包)
|
||||
ignore=shutil.ignore_patterns(".mimosa", "*.log", "zcode数据", "openclaw"),
|
||||
)
|
||||
print("复制目录 ai助手(ZCode 引擎 zcode 发行包+node+预置+启停cmd,排除 .mimosa/日志/zcode数据运行时目录)")
|
||||
else:
|
||||
print("跳过 ai助手(源目录不存在)")
|
||||
|
||||
# ===== 会员服务器:不进包(2026-09-17 用户要求停进包;2026-09-19 曾恢复,2026-09-22 用户
|
||||
# 明确长期规则=不打包并已从 release 目录手动删除——客户端默认连 https://ss61.odn.cc
|
||||
# 云端,部署套件单独交付,setup.iss 条目同步删除) =====
|
||||
print("\n发布目录:", REL)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,43 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""PDF扒图图标(Pillow 手绘,白文档+红折角+PDF 红标签,64x64 4x 超采样)。"""
|
||||
import os
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件"
|
||||
OUT = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "icons", "pdf-grab.png")
|
||||
|
||||
S = 4
|
||||
RED = (214, 69, 65, 255) # PDF 红
|
||||
RED_D = (192, 57, 43, 255) # 深红描边
|
||||
INK = (61, 67, 74, 255)
|
||||
|
||||
img = Image.new("RGBA", (64 * S, 64 * S), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(img)
|
||||
|
||||
def P(x, y, w, h, r, fill=None, outline=None, width=0):
|
||||
d.rounded_rectangle([x * S, y * S, (x + w) * S, (y + h) * S], radius=r * S,
|
||||
fill=fill, outline=outline, width=width * S)
|
||||
|
||||
def L(pts, color, w):
|
||||
d.line([(x * S, y * S) for x, y in pts], fill=color, width=w * S, joint="curve")
|
||||
|
||||
# 文档(带折角路径)
|
||||
doc = [(14, 6), (36, 6), (48, 18), (48, 58), (14, 58)]
|
||||
d.polygon([(x * S, y * S) for x, y in doc], fill=(255, 255, 255, 255))
|
||||
d.line([(x * S, y * S) for x, y in doc] + [(14 * S, 6 * S)], fill=RED_D, width=3 * S, joint="curve")
|
||||
L([(36, 6), (36, 18), (48, 18)], RED_D, 3)
|
||||
|
||||
# 内容线
|
||||
L([(20, 26), (42, 26)], (185, 190, 195, 255), 2)
|
||||
L([(20, 33), (42, 33)], (185, 190, 195, 255), 2)
|
||||
|
||||
# PDF 红标签(左下,压住文档边缘)
|
||||
P(6, 38, 36, 18, 3, fill=RED)
|
||||
try:
|
||||
font = ImageFont.truetype("msyhbd.ttc", 11 * S)
|
||||
except Exception:
|
||||
font = ImageFont.truetype("arialbd.ttf", 11 * S)
|
||||
d.text((24 * S, 47 * S), "PDF", font=font, fill=(255, 255, 255, 255), anchor="mm")
|
||||
|
||||
img.resize((64, 64), Image.LANCZOS).save(OUT)
|
||||
print("pdf-grab.png ->", OUT)
|
||||
@@ -0,0 +1,266 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""功能区 24 个按钮图标生成器。
|
||||
SVG 设计稿(语义化,统一风格) → svglib 栅格化 64x64 透明 PNG → bin\Debug\icons\。
|
||||
幂等可重跑;svg 定义即源档(同步存一份到 icons/svg 供后续编辑)。
|
||||
风格规范:viewBox 0 0 64 64;主色 #4A4A4A;强调绿 #217346(=功能区 AccentColor);
|
||||
删除红 #D83931;母排铜 #C8791E;线宽 4 圆角;不用白色填充(保透明底)。"""
|
||||
import os
|
||||
import io
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件"
|
||||
SVG_DIR = os.path.join(ROOT, "成套报价软件", "icons", "svg")
|
||||
PNG_DIR = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "icons")
|
||||
|
||||
G = "#4A4A4A" # 主体深灰
|
||||
GR = "#217346" # 强调绿
|
||||
RD = "#D83931" # 删除红
|
||||
CU = "#C8791E" # 铜排橙
|
||||
BL = "#2B579A" # 文档蓝(点缀)
|
||||
|
||||
W = 'stroke="%s" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" fill="none"' % G
|
||||
|
||||
def svg(body):
|
||||
return ('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">\n' + body + "\n</svg>\n")
|
||||
|
||||
# ---- 图标定义: name -> SVG body (64x64) ----
|
||||
ICONS = {}
|
||||
|
||||
# 文档底形(多图标复用): 纸张
|
||||
def doc(x=14, y=8, w=30, h=44, fold=True, color=G):
|
||||
f = ('<path d="M%d %d h%d l%d %d v%d h-%d z" %s stroke="%s"/>' %
|
||||
(x, y, w - 10, 10, 10, h - 10, w, W, color)) if fold else \
|
||||
('<rect x="%d" y="%d" width="%d" height="%d" rx="3" %s stroke="%s"/>' % (x, y, w, h, W, color))
|
||||
return f
|
||||
|
||||
# 1 button1 新建项目: 文档 + 绿色加号
|
||||
ICONS["new-project"] = svg(
|
||||
doc() +
|
||||
'<line x1="18" y1="24" x2="34" y2="24" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="18" y1="32" x2="30" y2="32" %s stroke="%s"/>' % (W, G) +
|
||||
'<circle cx="46" cy="44" r="13" fill="%s"/>' % GR +
|
||||
'<line x1="46" y1="37" x2="46" y2="51" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>' +
|
||||
'<line x1="39" y1="44" x2="53" y2="44" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round"/>')
|
||||
|
||||
# 2 button2 修改项目: 文档 + 笔(斜置)
|
||||
ICONS["edit-project"] = svg(
|
||||
doc() +
|
||||
'<line x1="18" y1="20" x2="28" y2="20" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="18" y1="28" x2="26" y2="28" %s stroke="%s"/>' % (W, G) +
|
||||
'<path d="M36 46 L52 30 L58 36 L42 52 L34 54 Z" fill="%s" stroke="%s" stroke-width="2"/>' % (CU, "#9C5A10") +
|
||||
'<line x1="49" y1="33" x2="55" y2="39" stroke="#9C5A10" stroke-width="2"/>')
|
||||
|
||||
# 3 button3 删除项目: 垃圾桶
|
||||
ICONS["delete-project"] = svg(
|
||||
'<path d="M16 20 h32 l-3 36 a4 4 0 0 1 -4 4 h-18 a4 4 0 0 1 -4 -4 z" %s stroke="%s"/>' % (W, RD) +
|
||||
'<line x1="12" y1="20" x2="52" y2="20" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % RD +
|
||||
'<line x1="26" y1="20" x2="26" y2="14" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % RD +
|
||||
'<line x1="38" y1="20" x2="38" y2="14" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % RD +
|
||||
'<line x1="26" y1="30" x2="27" y2="50" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % RD +
|
||||
'<line x1="38" y1="30" x2="37" y2="50" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % RD)
|
||||
|
||||
# 4 button4 打开项目(项目页): 打开的文件夹
|
||||
ICONS["open-project"] = svg(
|
||||
'<path d="M8 16 h14 l5 6 h21 v8 h-40 z" %s stroke="%s"/>' % (W, G) +
|
||||
'<path d="M10 30 h46 l-6 20 h-36 z" %s stroke="%s"/>' % (W, GR) +
|
||||
'<line x1="18" y1="38" x2="44" y2="38" stroke="%s" stroke-width="3" stroke-linecap="round" opacity="0.55"/>' % GR)
|
||||
|
||||
# 5 button5 复制项目: 双层文档
|
||||
ICONS["copy-project"] = svg(
|
||||
'<rect x="22" y="8" width="28" height="38" rx="3" %s stroke="%s"/>' % (W, G) +
|
||||
'<path d="M14 18 v36 h28" %s stroke="%s"/>' % (W, GR) +
|
||||
'<line x1="29" y1="18" x2="43" y2="18" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % G +
|
||||
'<line x1="29" y1="26" x2="43" y2="26" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % G +
|
||||
'<line x1="29" y1="34" x2="38" y2="34" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % G)
|
||||
|
||||
# 6 button8 导入项目: 容器+向下箭头(进箱)
|
||||
ICONS["import-project"] = svg(
|
||||
'<path d="M10 36 h44 v14 a6 6 0 0 1 -6 6 h-32 a6 6 0 0 1 -6 -6 z" %s stroke="%s"/>' % (W, GR) +
|
||||
'<line x1="32" y1="6" x2="32" y2="26" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % GR +
|
||||
'<path d="M24 20 l8 8 l8 -8" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR)
|
||||
|
||||
# 7 button7 导出项目: 容器+向上箭头(出箱)
|
||||
ICONS["export-project"] = svg(
|
||||
'<path d="M10 36 h44 v14 a6 6 0 0 1 -6 6 h-32 a6 6 0 0 1 -6 -6 z" %s stroke="%s"/>' % (W, BL) +
|
||||
'<line x1="32" y1="26" x2="32" y2="6" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % BL +
|
||||
'<path d="M24 12 l8 -8 l8 8" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % BL)
|
||||
|
||||
# 8 button9 打开项目(报价页): 文件夹+文档探出
|
||||
ICONS["open-quote"] = svg(
|
||||
'<path d="M8 14 h14 l5 6 h21 v8 h-40 z" %s stroke="%s"/>' % (W, G) +
|
||||
'<rect x="26" y="10" width="18" height="24" rx="2" fill="%s" stroke="%s" stroke-width="3"/>' % ("#E8F2EC", GR) +
|
||||
'<line x1="30" y1="17" x2="40" y2="17" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % GR +
|
||||
'<line x1="30" y1="24" x2="36" y2="24" stroke="%s" stroke-width="3" stroke-linecap="round"/>' % GR +
|
||||
'<path d="M10 28 h46 l-6 20 h-36 z" %s stroke="%s"/>' % (W, G))
|
||||
|
||||
# 9 button10 修改项目(报价页): 文档+笔+绿点(区分项目页)
|
||||
ICONS["edit-quote"] = svg(
|
||||
doc() +
|
||||
'<line x1="18" y1="20" x2="28" y2="20" %s stroke="%s"/>' % (W, G) +
|
||||
'<circle cx="24" cy="46" r="11" fill="%s"/>' % GR +
|
||||
'<path d="M34 42 L48 28 L54 34 L40 48 L33 50 Z" fill="%s" stroke="%s" stroke-width="2"/>' % (CU, "#9C5A10"))
|
||||
|
||||
# 10 button17 保存: 软盘(绿色)
|
||||
ICONS["save"] = svg(
|
||||
'<path d="M10 10 h36 l8 8 v36 h-44 z" %s stroke="%s"/>' % (W, GR) +
|
||||
'<rect x="20" y="10" width="20" height="14" %s stroke="%s"/>' % (W, GR) +
|
||||
'<rect x="20" y="36" width="24" height="18" %s stroke="%s"/>' % (W, GR) +
|
||||
'<line x1="38" y1="12" x2="38" y2="20" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % GR)
|
||||
|
||||
# 11 button18 导入报价: 文档+向下箭头
|
||||
ICONS["import-quote"] = svg(
|
||||
'<rect x="10" y="8" width="30" height="44" rx="3" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="17" y1="18" x2="33" y2="18" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="17" y1="26" x2="29" y2="26" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="48" y1="30" x2="48" y2="54" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % GR +
|
||||
'<path d="M40 48 l8 8 l8 -8" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR)
|
||||
|
||||
# 12 button16 甲方报表: 表格+绿色对勾
|
||||
ICONS["report"] = svg(
|
||||
'<rect x="8" y="10" width="48" height="44" rx="3" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="8" y1="22" x2="56" y2="22" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="8" y1="34" x2="56" y2="34" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="24" y1="22" x2="24" y2="54" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="40" y1="22" x2="40" y2="54" %s stroke="%s"/>' % (W, G) +
|
||||
'<circle cx="47" cy="45" r="12" fill="%s"/>' % GR +
|
||||
'<path d="M41 45 l5 5 l8 -9" stroke="#FFFFFF" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>')
|
||||
|
||||
# 13 button15 调母排: 三根铜排+调节箭头
|
||||
ICONS["busbar"] = svg(
|
||||
'<rect x="8" y="14" width="48" height="8" rx="3" fill="%s"/>' % CU +
|
||||
'<rect x="8" y="30" width="48" height="8" rx="3" fill="%s" opacity="0.85"/>' % CU +
|
||||
'<rect x="8" y="46" width="34" height="8" rx="3" fill="%s" opacity="0.7"/>' % CU +
|
||||
'<path d="M46 56 l6 -6 l6 6" stroke="%s" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % G)
|
||||
|
||||
# 14 button13 调系数: 双滑块调节
|
||||
ICONS["factor"] = svg(
|
||||
'<line x1="12" y1="22" x2="52" y2="22" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="12" y1="42" x2="52" y2="42" %s stroke="%s"/>' % (W, G) +
|
||||
'<circle cx="40" cy="22" r="7" fill="%s" stroke="%s" stroke-width="3"/>' % (GR, GR) +
|
||||
'<circle cx="24" cy="42" r="7" fill="%s" stroke="%s" stroke-width="3"/>' % (BL, BL))
|
||||
|
||||
# 15 button33 元件排序: 列表+上下箭头
|
||||
ICONS["sort-components"] = svg(
|
||||
'<circle cx="16" cy="16" r="4" fill="%s"/>' % G +
|
||||
'<circle cx="16" cy="32" r="4" fill="%s"/>' % G +
|
||||
'<circle cx="16" cy="48" r="4" fill="%s"/>' % G +
|
||||
'<line x1="26" y1="16" x2="44" y2="16" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G +
|
||||
'<line x1="26" y1="32" x2="40" y2="32" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G +
|
||||
'<line x1="26" y1="48" x2="36" y2="48" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G +
|
||||
'<path d="M50 20 l6 -6 l6 6" stroke="%s" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR +
|
||||
'<path d="M50 44 l6 6 l6 -6" stroke="%s" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR)
|
||||
|
||||
# 16 button29 模板费用: 模板网格+¥
|
||||
ICONS["template-cost"] = svg(
|
||||
'<rect x="8" y="8" width="48" height="48" rx="4" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="8" y1="24" x2="56" y2="24" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="8" y1="40" x2="36" y2="40" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="30" y1="24" x2="30" y2="56" %s stroke="%s"/>' % (W, G) +
|
||||
'<circle cx="47" cy="45" r="13" fill="%s"/>' % GR +
|
||||
'<text x="47" y="53" font-family="Segoe UI, Arial" font-size="20" font-weight="bold" fill="#FFFFFF" text-anchor="middle">¥</text>')
|
||||
|
||||
# 17 button11 CAD扒图: 齿轮+放大镜
|
||||
ICONS["cad-grab"] = svg(
|
||||
'<circle cx="26" cy="26" r="9" %s stroke="%s"/>' % (W, G) +
|
||||
'<path d="M26 8 v6 M26 38 v6 M8 26 h6 M38 26 h6 M13 13 l4.5 4.5 M34.5 34.5 L39 39 M39 13 l-4.5 4.5 M17.5 34.5 L13 39" '
|
||||
'stroke="%s" stroke-width="5" stroke-linecap="round"/>' % G +
|
||||
'<circle cx="44" cy="44" r="10" %s stroke="%s"/>' % (W, GR) +
|
||||
'<line x1="51" y1="51" x2="58" y2="58" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % GR)
|
||||
|
||||
# 18 btn登录: 人形+门框
|
||||
ICONS["login"] = svg(
|
||||
'<path d="M40 8 h14 v48 h-14" %s stroke="%s"/>' % (W, G) +
|
||||
'<circle cx="22" cy="24" r="8" %s stroke="%s"/>' % (W, GR) +
|
||||
'<path d="M8 52 a14 14 0 0 1 28 0" %s stroke="%s"/>' % (W, GR) +
|
||||
'<line x1="48" y1="32" x2="48" y2="36" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G)
|
||||
|
||||
# 19 btn修改密码: 锁+钥匙孔
|
||||
ICONS["password"] = svg(
|
||||
'<rect x="14" y="28" width="36" height="28" rx="4" %s stroke="%s"/>' % (W, GR) +
|
||||
'<path d="M22 28 v-8 a10 10 0 0 1 20 0 v8" %s stroke="%s"/>' % (W, GR) +
|
||||
'<circle cx="32" cy="40" r="4" fill="%s"/>' % GR +
|
||||
'<line x1="32" y1="44" x2="32" y2="50" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % GR)
|
||||
|
||||
# 20 btn会员信息: 人像卡片
|
||||
ICONS["member"] = svg(
|
||||
'<rect x="8" y="14" width="48" height="36" rx="4" %s stroke="%s"/>' % (W, G) +
|
||||
'<circle cx="24" cy="30" r="6" %s stroke="%s"/>' % (W, GR) +
|
||||
'<path d="M14 46 a10 10 0 0 1 20 0" %s stroke="%s"/>' % (W, GR) +
|
||||
'<line x1="40" y1="26" x2="50" y2="26" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G +
|
||||
'<line x1="40" y1="36" x2="50" y2="36" stroke="%s" stroke-width="4" stroke-linecap="round"/>' % G)
|
||||
|
||||
# 21 btn企业信息: 办公楼
|
||||
ICONS["company"] = svg(
|
||||
'<rect x="12" y="10" width="28" height="46" %s stroke="%s"/>' % (W, G) +
|
||||
'<rect x="40" y="26" width="14" height="30" %s stroke="%s"/>' % (W, GR) +
|
||||
'<line x1="19" y1="20" x2="25" y2="20" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
|
||||
'<line x1="31" y1="20" x2="37" y2="20" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
|
||||
'<line x1="19" y1="30" x2="25" y2="30" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
|
||||
'<line x1="31" y1="30" x2="37" y2="30" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
|
||||
'<line x1="19" y1="40" x2="25" y2="40" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
|
||||
'<line x1="31" y1="40" x2="37" y2="40" stroke="%s" stroke-width="3.5" stroke-linecap="round"/>' % G +
|
||||
'<rect x="22" y="48" width="8" height="8" %s stroke="%s"/>' % (W, G))
|
||||
|
||||
# 22 btn保存布局: 网格布局+绿色对勾
|
||||
ICONS["save-layout"] = svg(
|
||||
'<rect x="8" y="8" width="48" height="48" rx="4" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="8" y1="26" x2="56" y2="26" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="30" y1="26" x2="30" y2="56" %s stroke="%s"/>' % (W, G) +
|
||||
'<rect x="12" y="12" width="14" height="10" fill="%s" opacity="0.85"/>' % GR +
|
||||
'<circle cx="45" cy="43" r="12" fill="%s"/>' % GR +
|
||||
'<path d="M39 43 l5 5 l8 -9" stroke="#FFFFFF" stroke-width="4" fill="none" stroke-linecap="round" stroke-linejoin="round"/>')
|
||||
|
||||
# 23 btn备份: 数据库+向右箭头
|
||||
ICONS["backup"] = svg(
|
||||
'<ellipse cx="24" cy="16" rx="14" ry="6" %s stroke="%s"/>' % (W, G) +
|
||||
'<path d="M10 16 v30 a14 6 0 0 0 28 0 v-30" %s stroke="%s"/>' % (W, G) +
|
||||
'<path d="M10 31 a14 6 0 0 0 28 0" %s stroke="%s"/>' % (W, G) +
|
||||
'<line x1="40" y1="40" x2="56" y2="40" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % GR +
|
||||
'<path d="M50 34 l8 6 l-8 6" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % GR)
|
||||
|
||||
# 24 btn恢复: 数据库+向左回箭头
|
||||
ICONS["restore"] = svg(
|
||||
'<ellipse cx="40" cy="16" rx="14" ry="6" %s stroke="%s"/>' % (W, GR) +
|
||||
'<path d="M26 16 v30 a14 6 0 0 0 28 0 v-30" %s stroke="%s"/>' % (W, GR) +
|
||||
'<path d="M26 31 a14 6 0 0 0 28 0" %s stroke="%s"/>' % (W, GR) +
|
||||
'<line x1="24" y1="40" x2="8" y2="40" stroke="%s" stroke-width="5" stroke-linecap="round"/>' % G +
|
||||
'<path d="M14 34 l-8 6 l8 6" stroke="%s" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>' % G)
|
||||
|
||||
# 按钮 Name → 图标文件映射(与 Designer.cs 集合一致)
|
||||
BIND = {
|
||||
"button1": "new-project", "button2": "edit-project", "button3": "delete-project",
|
||||
"button4": "open-project", "button5": "copy-project", "button7": "export-project",
|
||||
"button8": "import-project", "button9": "open-quote", "button10": "edit-quote",
|
||||
"button17": "save", "button18": "import-quote", "button16": "report",
|
||||
"button15": "busbar", "button13": "factor", "button33": "sort-components",
|
||||
"button29": "template-cost", "button11": "cad-grab",
|
||||
"btn登录": "login", "btn修改密码": "password", "btn会员信息": "member",
|
||||
"btn企业信息": "company", "btn保存布局": "save-layout",
|
||||
"btn备份": "backup", "btn恢复": "restore",
|
||||
}
|
||||
|
||||
|
||||
def render_all():
|
||||
from svglib.svglib import svg2rlg
|
||||
from reportlab.graphics import renderPM
|
||||
os.makedirs(SVG_DIR, exist_ok=True)
|
||||
os.makedirs(PNG_DIR, exist_ok=True)
|
||||
ok, fail = 0, []
|
||||
for name, body in ICONS.items():
|
||||
sp = os.path.join(SVG_DIR, name + ".svg")
|
||||
with io.open(sp, "w", encoding="utf-8") as f:
|
||||
f.write(svg(body) if not body.startswith("<svg") else body)
|
||||
try:
|
||||
d = svg2rlg(sp)
|
||||
# 64x64 输出(svglib 默认按 72dpi 走 1:1,viewBox 64 → 64px)
|
||||
pp = os.path.join(PNG_DIR, name + ".png")
|
||||
renderPM.drawToFile(d, pp, fmt="PNG", dpi=72, bg=0x00000000)
|
||||
ok += 1
|
||||
except Exception as ex:
|
||||
fail.append((name, str(ex)[:120]))
|
||||
print("PNG 生成: %d 成功 / %d 失败, 输出 %s" % (ok, len(fail), PNG_DIR))
|
||||
for n, e in fail:
|
||||
print(" [失败]", n, e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
render_all()
|
||||
@@ -0,0 +1,118 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""功能区 24 图标最终版:Lucide 开源图标库(ISC 可商用,专业设计)。
|
||||
管线:jsdelivr 拉取 lucide-static SVG → 替换 currentColor 为语义色 → Edge 无头渲染 256px → 缩 64px。
|
||||
文件名不变(bin\\Debug\\icons\\*.png),Designer.cs 的 FromFile 路径不用改。
|
||||
404 的图标名会打印出来供换名重跑。"""
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from PIL import Image
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件"
|
||||
PNG_DIR = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "icons")
|
||||
SVG_DIR = os.path.join(ROOT, "成套报价软件", "icons", "svg", "lucide")
|
||||
EDGE = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
|
||||
VER = "0.525.0"
|
||||
URL = "https://cdn.jsdelivr.net/npm/lucide-static@%s/icons/%%s.svg" % VER
|
||||
|
||||
INK = "#3D434A" # 墨青灰(默认)
|
||||
GRN = "#217346" # 强调绿(=功能区 AccentColor)
|
||||
COP = "#C8791E" # 铜橙
|
||||
RED = "#D64541" # 警示红
|
||||
BLU = "#2B579A" # 点缀蓝
|
||||
|
||||
# 按钮 → (lucide 图标名, 颜色)
|
||||
BIND = {
|
||||
"new-project": ("file-plus-2", INK),
|
||||
"edit-project": ("file-pen", INK),
|
||||
"delete-project": ("trash-2", RED),
|
||||
"open-project": ("folder-open", INK),
|
||||
"copy-project": ("files", INK),
|
||||
"import-project": ("import", GRN),
|
||||
"export-project": ("export", BLU),
|
||||
"open-quote": ("folder-open", INK), # button9 同款
|
||||
"edit-quote": ("file-pen", INK), # button10 同款
|
||||
"save": ("save", GRN),
|
||||
"import-quote": ("file-down", GRN),
|
||||
"report": ("table-cells", INK),
|
||||
"busbar": ("align-justify", COP), # 三横线=三根铜排
|
||||
"factor": ("sliders-horizontal", INK),
|
||||
"sort-components": ("arrow-up-narrow-wide", INK),
|
||||
"template-cost": ("badge-japanese-yen", GRN),
|
||||
"cad-grab": ("drafting-compass", INK),
|
||||
"login": ("log-in", GRN),
|
||||
"password": ("key-round", INK),
|
||||
"member": ("id-card", INK),
|
||||
"company": ("building-2", INK),
|
||||
"save-layout": ("layout-template", INK),
|
||||
"backup": ("database-backup", GRN),
|
||||
"restore": ("archive-restore", INK),
|
||||
}
|
||||
|
||||
|
||||
def fetch(name):
|
||||
import urllib.request
|
||||
req = urllib.request.Request(URL % name, headers={"User-Agent": "Mozilla/5.0"})
|
||||
with urllib.request.urlopen(req, timeout=30) as r:
|
||||
return r.read().decode("utf-8")
|
||||
|
||||
|
||||
def process(svg_text, color):
|
||||
# 去注释/class,currentColor→语义色,尺寸改 256(viewBox 24 不动)
|
||||
svg_text = re.sub(r"<!--.*?-->\s*", "", svg_text, flags=re.S)
|
||||
svg_text = svg_text.replace('class="lucide lucide-%s"' % "", "")
|
||||
svg_text = re.sub(r'\s+class="[^"]*"', "", svg_text)
|
||||
svg_text = svg_text.replace('stroke="currentColor"', 'stroke="%s"' % color)
|
||||
svg_text = svg_text.replace('width="24"', 'width="256"').replace('height="24"', 'height="256"')
|
||||
return svg_text
|
||||
|
||||
|
||||
def render_edge(name, svg_text):
|
||||
sp = os.path.join(SVG_DIR, name + ".svg")
|
||||
with io.open(sp, "w", encoding="utf-8") as f:
|
||||
f.write(svg_text)
|
||||
tmp = tempfile.mkdtemp(prefix="lucide_")
|
||||
try:
|
||||
tmp_svg = os.path.join(tmp, "i.svg")
|
||||
tmp_png = os.path.join(tmp, "i.png")
|
||||
shutil.copy(sp, tmp_svg)
|
||||
subprocess.run(
|
||||
[EDGE, "--headless", "--disable-gpu", "--no-sandbox",
|
||||
"--user-data-dir=" + os.path.join(tmp, "p"),
|
||||
"--screenshot=" + tmp_png, "--window-size=256,256",
|
||||
"--default-background-color=00000000",
|
||||
"file:///" + tmp_svg.replace("\\", "/")],
|
||||
capture_output=True, timeout=40)
|
||||
if not os.path.exists(tmp_png):
|
||||
raise RuntimeError("Edge 渲染失败")
|
||||
Image.open(tmp_png).convert("RGBA").resize((64, 64), Image.LANCZOS)\
|
||||
.save(os.path.join(PNG_DIR, name + ".png"))
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs(SVG_DIR, exist_ok=True)
|
||||
os.makedirs(PNG_DIR, exist_ok=True)
|
||||
ok, missing = 0, []
|
||||
for name, (icon, color) in BIND.items():
|
||||
try:
|
||||
raw = fetch(icon)
|
||||
if "<svg" not in raw:
|
||||
raise RuntimeError("非 SVG 响应")
|
||||
render_edge(name, process(raw, color))
|
||||
ok += 1
|
||||
print(" ✓ %-18s <- %s" % (name, icon))
|
||||
except Exception as ex:
|
||||
missing.append((name, icon, str(ex)[:80]))
|
||||
print("\n完成 %d/%d" % (ok, len(BIND)))
|
||||
for n, i, e in missing:
|
||||
print(" [失败] %s <- %s: %s" % (n, i, e))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,269 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""功能区 24 个按钮图标:Pillow 等价绘制(本机无 cairo,SVG 无法栅格化——
|
||||
icons/svg/*.svg 为设计稿存档,本脚本按同一几何画 256x256 超采样 → 64x64 透明 PNG)。
|
||||
输出 bin\\Debug\\icons\\*.png,文件名与 make_ribbon_icons.py 的 BIND 映射一致。"""
|
||||
import os
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件"
|
||||
OUT = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "icons")
|
||||
SVG_DIR = os.path.join(ROOT, "成套报价软件", "icons", "svg")
|
||||
|
||||
S = 4 # 256 = 64*4 超采样
|
||||
G = (74, 74, 74, 255) # 主体深灰
|
||||
GR = (33, 115, 70, 255) # 强调绿
|
||||
RD = (216, 57, 49, 255) # 删除红
|
||||
CU = (200, 121, 30, 255) # 铜排橙
|
||||
BL = (43, 87, 154, 255) # 点缀蓝
|
||||
WH = (255, 255, 255, 255)
|
||||
PGL = (232, 242, 236, 255) # 淡绿底
|
||||
|
||||
def L(v): # 64 坐标 → 256(取整,Pillow 不收浮点)
|
||||
return int(round(v * S))
|
||||
|
||||
def line(d, pts, color, w=4):
|
||||
d.line([(L(x), L(y)) for x, y in pts], fill=color, width=L(w), joint="curve")
|
||||
|
||||
def rrect(d, x, y, w2, h2, r, color, width=4, fill=None):
|
||||
d.rounded_rectangle([L(x), L(y), L(x + w2), L(y + h2)], radius=L(r),
|
||||
outline=color, width=L(width), fill=fill)
|
||||
|
||||
def rect(d, x, y, w2, h2, color, width=4, fill=None):
|
||||
d.rectangle([L(x), L(y), L(x + w2), L(y + h2)], outline=color, width=L(width), fill=fill)
|
||||
|
||||
def circle(d, cx, cy, r, color=None, width=4, fill=None):
|
||||
d.ellipse([L(cx - r), L(cy - r), L(cx + r), L(cy + r)],
|
||||
outline=color, width=L(width) if color else 0, fill=fill)
|
||||
|
||||
def ell(d, cx, cy, rx, ry, color, width=4, fill=None):
|
||||
d.ellipse([L(cx - rx), L(cy - ry), L(cx + rx), L(cy + ry)], outline=color,
|
||||
width=L(width), fill=fill)
|
||||
|
||||
def arc(d, cx, cy, r, a1, a2, color, width=4):
|
||||
d.arc([L(cx - r), L(cy - r), L(cx + r), L(cy + r)], start=a1, end=a2,
|
||||
fill=color, width=L(width))
|
||||
|
||||
def doc_outline(d, x=14, y=8, w2=30, h2=44, color=G):
|
||||
"""折角文档轮廓"""
|
||||
line(d, [(x, y), (x + w2 - 10, y), (x + w2, y + 10), (x + w2, y + h2),
|
||||
(x, y + h2), (x, y)], color)
|
||||
|
||||
def poly(d, pts, color, width=4, fill=None, close=True):
|
||||
p = [(L(x), L(y)) for x, y in pts]
|
||||
if fill:
|
||||
d.polygon(p, fill=fill)
|
||||
if close:
|
||||
p = p + [p[0]]
|
||||
d.line(p, fill=color, width=L(width), joint="curve")
|
||||
|
||||
# ============ 24 个图标(与 icons/svg 设计稿同几何) ============
|
||||
|
||||
def i_new_project(d):
|
||||
doc_outline(d)
|
||||
line(d, [(18, 24), (34, 24)], G)
|
||||
line(d, [(18, 32), (30, 32)], G)
|
||||
circle(d, 46, 44, 13, fill=GR)
|
||||
line(d, [(46, 37), (46, 51)], WH)
|
||||
line(d, [(39, 44), (53, 44)], WH)
|
||||
|
||||
def i_edit_project(d):
|
||||
doc_outline(d)
|
||||
line(d, [(18, 20), (28, 20)], G)
|
||||
line(d, [(18, 28), (26, 28)], G)
|
||||
poly(d, [(36, 46), (52, 30), (58, 36), (42, 52), (34, 54)], (156, 90, 16, 255), width=2, fill=CU)
|
||||
line(d, [(49, 33), (55, 39)], (156, 90, 16, 255), w=2)
|
||||
|
||||
def i_delete_project(d):
|
||||
poly(d, [(16, 20), (48, 20), (45, 56), (23, 56)], RD, width=4)
|
||||
line(d, [(12, 20), (52, 20)], RD, w=5)
|
||||
line(d, [(26, 20), (26, 14)], RD)
|
||||
line(d, [(38, 20), (38, 14)], RD)
|
||||
line(d, [(26, 30), (27, 50)], RD)
|
||||
line(d, [(38, 30), (37, 50)], RD)
|
||||
|
||||
def i_open_project(d):
|
||||
poly(d, [(8, 16), (22, 16), (27, 22), (48, 22), (48, 30), (8, 30)], G, width=4)
|
||||
poly(d, [(10, 30), (56, 30), (50, 50), (14, 50)], GR, width=4)
|
||||
line(d, [(18, 38), (44, 38)], (150, 190, 168, 255), w=3)
|
||||
|
||||
def i_copy_project(d):
|
||||
rrect(d, 22, 8, 28, 38, 3, G)
|
||||
poly(d, [(14, 18), (14, 54), (42, 54)], GR, width=4, close=False)
|
||||
line(d, [(29, 18), (43, 18)], G, w=3)
|
||||
line(d, [(29, 26), (43, 26)], G, w=3)
|
||||
line(d, [(29, 34), (38, 34)], G, w=3)
|
||||
|
||||
def _box_u(d, color):
|
||||
"""开口容器:U 形(左、底、右边,顶部开放)"""
|
||||
line(d, [(10, 36), (10, 50), (16, 56), (48, 56), (54, 50), (54, 36)], color, w=4)
|
||||
|
||||
def i_import_project(d):
|
||||
_box_u(d, GR)
|
||||
line(d, [(32, 6), (32, 26)], GR, w=5)
|
||||
poly(d, [(24, 20), (32, 28), (40, 20)], GR, width=5, close=False)
|
||||
|
||||
def i_export_project(d):
|
||||
_box_u(d, BL)
|
||||
line(d, [(32, 26), (32, 6)], BL, w=5)
|
||||
poly(d, [(24, 12), (32, 4), (40, 12)], BL, width=5, close=False)
|
||||
|
||||
def i_open_quote(d):
|
||||
poly(d, [(8, 14), (22, 14), (27, 20), (48, 20), (48, 28), (8, 28)], G, width=4)
|
||||
rrect(d, 26, 10, 18, 24, 2, GR, width=3, fill=PGL)
|
||||
line(d, [(30, 17), (40, 17)], GR, w=3)
|
||||
line(d, [(30, 24), (36, 24)], GR, w=3)
|
||||
poly(d, [(10, 28), (56, 28), (50, 48), (14, 48)], G, width=4)
|
||||
|
||||
def i_edit_quote(d):
|
||||
doc_outline(d)
|
||||
line(d, [(18, 20), (28, 20)], G)
|
||||
circle(d, 24, 46, 11, fill=GR)
|
||||
poly(d, [(34, 42), (48, 28), (54, 34), (40, 48), (33, 50)], (156, 90, 16, 255), width=2, fill=CU)
|
||||
|
||||
def i_save(d):
|
||||
poly(d, [(10, 10), (46, 10), (54, 18), (54, 54), (10, 54)], GR, width=4)
|
||||
rect(d, 20, 10, 20, 14, GR, width=4)
|
||||
rect(d, 20, 36, 24, 18, GR, width=4)
|
||||
line(d, [(38, 12), (38, 20)], GR)
|
||||
|
||||
def i_import_quote(d):
|
||||
rrect(d, 10, 8, 30, 44, 3, G)
|
||||
line(d, [(17, 18), (33, 18)], G)
|
||||
line(d, [(17, 26), (29, 26)], G)
|
||||
line(d, [(48, 30), (48, 54)], GR, w=5)
|
||||
poly(d, [(40, 48), (48, 56), (56, 48)], GR, width=5, close=False)
|
||||
|
||||
def i_report(d):
|
||||
rrect(d, 8, 10, 48, 44, 3, G)
|
||||
line(d, [(8, 22), (56, 22)], G)
|
||||
line(d, [(8, 34), (56, 34)], G)
|
||||
line(d, [(24, 22), (24, 54)], G)
|
||||
line(d, [(40, 22), (40, 54)], G)
|
||||
circle(d, 47, 45, 12, fill=GR)
|
||||
poly(d, [(41, 45), (46, 50), (54, 41)], WH, width=4, close=False)
|
||||
|
||||
def i_busbar(d):
|
||||
# 三根铜排(fill 直接画,深浅表现层次)+右下调节箭头
|
||||
d.rounded_rectangle([L(8), L(14), L(56), L(22)], radius=L(3), fill=CU)
|
||||
d.rounded_rectangle([L(8), L(30), L(56), L(38)], radius=L(3), fill=(214, 149, 74, 255))
|
||||
d.rounded_rectangle([L(8), L(46), L(42), L(54)], radius=L(3), fill=(227, 177, 118, 255))
|
||||
poly(d, [(48, 58), (54, 52), (60, 58)], G, width=4, close=False)
|
||||
|
||||
def i_factor(d):
|
||||
line(d, [(12, 22), (52, 22)], G)
|
||||
line(d, [(12, 42), (52, 42)], G)
|
||||
circle(d, 40, 22, 7, GR, width=3, fill=GR)
|
||||
circle(d, 24, 42, 7, BL, width=3, fill=BL)
|
||||
|
||||
def i_sort_components(d):
|
||||
circle(d, 16, 16, 4, fill=G)
|
||||
circle(d, 16, 32, 4, fill=G)
|
||||
circle(d, 16, 48, 4, fill=G)
|
||||
line(d, [(26, 16), (44, 16)], G)
|
||||
line(d, [(26, 32), (40, 32)], G)
|
||||
line(d, [(26, 48), (36, 48)], G)
|
||||
poly(d, [(50, 20), (56, 14), (62, 20)], GR, width=4, close=False)
|
||||
poly(d, [(50, 44), (56, 50), (62, 44)], GR, width=4, close=False)
|
||||
|
||||
def i_template_cost(d):
|
||||
rrect(d, 8, 8, 48, 48, 4, G)
|
||||
line(d, [(8, 24), (56, 24)], G)
|
||||
line(d, [(8, 40), (36, 40)], G)
|
||||
line(d, [(30, 24), (30, 56)], G)
|
||||
circle(d, 47, 45, 13, fill=GR)
|
||||
try:
|
||||
font = ImageFont.truetype("msyhbd.ttc", L(22))
|
||||
except Exception:
|
||||
font = ImageFont.truetype("arialbd.ttf", L(22))
|
||||
d.text((L(47), L(45)), "¥", font=font, fill=WH, anchor="mm")
|
||||
|
||||
def i_cad_grab(d):
|
||||
circle(d, 26, 26, 9, G)
|
||||
for pts in [[(26, 8), (26, 14)], [(26, 38), (26, 44)], [(8, 26), (14, 26)],
|
||||
[(38, 26), (44, 26)], [(13, 13), (17.5, 17.5)], [(34.5, 34.5), (39, 39)],
|
||||
[(39, 13), (34.5, 17.5)], [(17.5, 34.5), (13, 39)]]:
|
||||
line(d, pts, G, w=5)
|
||||
circle(d, 44, 44, 10, GR)
|
||||
line(d, [(51, 51), (58, 58)], GR, w=5)
|
||||
|
||||
def i_login(d):
|
||||
poly(d, [(40, 8), (54, 8), (54, 56), (40, 56)], G, width=4, close=False)
|
||||
circle(d, 22, 24, 8, GR)
|
||||
arc(d, 22, 48, 14, 180, 360, GR)
|
||||
line(d, [(48, 32), (48, 36)], G)
|
||||
|
||||
def i_password(d):
|
||||
rrect(d, 14, 28, 36, 28, 4, GR)
|
||||
arc(d, 32, 28, 10, 180, 360, GR)
|
||||
line(d, [(22, 28), (22, 20)], GR)
|
||||
line(d, [(42, 28), (42, 20)], GR)
|
||||
circle(d, 32, 40, 4, fill=GR)
|
||||
line(d, [(32, 44), (32, 50)], GR)
|
||||
|
||||
def i_member(d):
|
||||
rrect(d, 8, 14, 48, 36, 4, G)
|
||||
circle(d, 24, 30, 6, GR)
|
||||
arc(d, 24, 43, 10, 180, 360, GR)
|
||||
line(d, [(40, 26), (50, 26)], G)
|
||||
line(d, [(40, 36), (50, 36)], G)
|
||||
|
||||
def i_company(d):
|
||||
rect(d, 12, 10, 28, 46, G)
|
||||
rect(d, 40, 26, 14, 30, GR)
|
||||
for y in (20, 30, 40):
|
||||
line(d, [(19, y), (25, y)], G, w=3.5)
|
||||
line(d, [(31, y), (37, y)], G, w=3.5)
|
||||
rect(d, 22, 48, 8, 8, G)
|
||||
|
||||
def i_save_layout(d):
|
||||
rrect(d, 8, 8, 48, 48, 4, G)
|
||||
line(d, [(8, 26), (56, 26)], G)
|
||||
line(d, [(30, 26), (30, 56)], G)
|
||||
d.rectangle([L(12), L(12), L(26), L(22)], fill=(150, 190, 168, 255))
|
||||
circle(d, 45, 43, 12, fill=GR)
|
||||
poly(d, [(39, 43), (44, 48), (52, 39)], WH, width=4, close=False)
|
||||
|
||||
def _db(d, cx, color):
|
||||
ell(d, cx, 16, 14, 6, color)
|
||||
line(d, [(cx - 14, 16), (cx - 14, 46)], color)
|
||||
line(d, [(cx + 14, 16), (cx + 14, 46)], color)
|
||||
arc(d, cx, 40, 14, 0, 180, color)
|
||||
arc(d, cx, 25, 14, 0, 180, color)
|
||||
|
||||
def i_backup(d):
|
||||
_db(d, 24, G)
|
||||
line(d, [(40, 40), (56, 40)], GR, w=5)
|
||||
poly(d, [(50, 34), (58, 40), (50, 46)], GR, width=5, close=False)
|
||||
|
||||
def i_restore(d):
|
||||
_db(d, 40, GR)
|
||||
line(d, [(24, 40), (8, 40)], G, w=5)
|
||||
poly(d, [(14, 34), (6, 40), (14, 46)], G, width=5, close=False)
|
||||
|
||||
ICONS = {
|
||||
"new-project": i_new_project, "edit-project": i_edit_project,
|
||||
"delete-project": i_delete_project, "open-project": i_open_project,
|
||||
"copy-project": i_copy_project, "export-project": i_export_project,
|
||||
"import-project": i_import_project, "open-quote": i_open_quote,
|
||||
"edit-quote": i_edit_quote, "save": i_save, "import-quote": i_import_quote,
|
||||
"report": i_report, "busbar": i_busbar, "factor": i_factor,
|
||||
"sort-components": i_sort_components, "template-cost": i_template_cost,
|
||||
"cad-grab": i_cad_grab, "login": i_login, "password": i_password,
|
||||
"member": i_member, "company": i_company, "save-layout": i_save_layout,
|
||||
"backup": i_backup, "restore": i_restore,
|
||||
}
|
||||
|
||||
def main():
|
||||
os.makedirs(OUT, exist_ok=True)
|
||||
ok = 0
|
||||
for name, fn in ICONS.items():
|
||||
img = Image.new("RGBA", (64 * S, 64 * S), (0, 0, 0, 0))
|
||||
d = ImageDraw.Draw(img)
|
||||
fn(d)
|
||||
img = img.resize((64, 64), Image.LANCZOS)
|
||||
img.save(os.path.join(OUT, name + ".png"))
|
||||
ok += 1
|
||||
print("Pillow 图标生成 %d 个 → %s" % (ok, OUT))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,290 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""功能区 24 图标(SVG 正式版):手写 SVG 设计稿 → Edge 无头渲染 256px 透明 PNG → 缩 64px。
|
||||
设计原则(frontend-design 技能):视觉词汇取自电气成套行业(图纸/柜体/铜排/元件符号);
|
||||
主描边 3px 圆角端点;每个图标 = 墨青灰线性主形 + 1 个实色焦点(绿/铜/红/蓝)。
|
||||
调色:墨青灰#3D434A 强调绿#217346(=功能区AccentColor) 铜橙#C8791E 警示红#D64541 点缀蓝#2B579A
|
||||
输出: bin\\Debug\\icons\\*.png(文件名不变,Designer.cs FromFile 路径不用改) + icons\\svg\\*.svg 设计稿。"""
|
||||
import os
|
||||
import io
|
||||
import subprocess
|
||||
import tempfile
|
||||
import shutil
|
||||
|
||||
from PIL import Image
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件"
|
||||
PNG_DIR = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "icons")
|
||||
SVG_DIR = os.path.join(ROOT, "成套报价软件", "icons", "svg")
|
||||
EDGE = r"C:\Program Files (x86)\Microsoft\Edge\Application\msedge.exe"
|
||||
|
||||
INK = "#3D434A" # 墨青灰
|
||||
GRN = "#217346" # 强调绿
|
||||
COP = "#C8791E" # 铜橙
|
||||
RED = "#D64541" # 警示红
|
||||
BLU = "#2B579A" # 点缀蓝
|
||||
PGR = "#E5F0EA" # 淡绿底
|
||||
LGR = "#9DBFAA" # 灰绿(弱线)
|
||||
|
||||
def S(body, stroke=INK, w=3):
|
||||
"""外层 svg: 256 输出 / 64 网格"""
|
||||
return ('<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 64 64">\n'
|
||||
+ body + "\n</svg>")
|
||||
|
||||
def L(pts, c=INK, w=3):
|
||||
p = " ".join("%g %g" % (x, y) for x, y in pts)
|
||||
return '<polyline points="%s" fill="none" stroke="%s" stroke-width="%g" stroke-linecap="round" stroke-linejoin="round"/>' % (p, c, w)
|
||||
|
||||
def R(x, y, w2, h2, rx, c=INK, sw=3, fill="none"):
|
||||
f = ' fill="%s"' % fill
|
||||
s = ' stroke="%s" stroke-width="%g"' % (c, sw) if sw else ""
|
||||
return '<rect x="%g" y="%g" width="%g" height="%g" rx="%g"%s%s/>' % (x, y, w2, h2, rx, f, s)
|
||||
|
||||
def C(cx, cy, r, c=INK, sw=3, fill="none"):
|
||||
f = ' fill="%s"' % fill
|
||||
s = ' stroke="%s" stroke-width="%g"' % (c, sw) if sw else ""
|
||||
return '<circle cx="%g" cy="%g" r="%g"%s%s/>' % (cx, cy, r, f, s)
|
||||
|
||||
def E(cx, cy, rx, ry, c=INK, sw=3, fill="none"):
|
||||
return '<ellipse cx="%g" cy="%g" rx="%g" ry="%g" fill="%s" stroke="%s" stroke-width="%g"/>' % (cx, cy, rx, ry, fill, c, sw)
|
||||
|
||||
def P(d, c=INK, w=3, fill="none"):
|
||||
s = ' stroke="%s" stroke-width="%g"' % (c, w) if w else ""
|
||||
f = ' fill="%s"' % fill
|
||||
return '<path d="%s"%s%s stroke-linecap="round" stroke-linejoin="round"/>' % (d, f, s)
|
||||
|
||||
def T(x, y, txt, size, c="#FFFFFF", weight="bold"):
|
||||
return ('<text x="%g" y="%g" font-family="Segoe UI, Microsoft YaHei, Arial" font-size="%g" '
|
||||
'font-weight="%s" fill="%s" text-anchor="middle" dominant-baseline="central">%s</text>'
|
||||
% (x, y, size, weight, c, txt))
|
||||
|
||||
# ---- 图标定义(64 网格) ----
|
||||
ICONS = {}
|
||||
|
||||
# 1 新建项目:完整折角图纸 + 右下绿加号徽标(白色隔离圈防穿插)
|
||||
ICONS["new-project"] = S(
|
||||
P("M12 6 H34 L46 18 V36 H12 Z") +
|
||||
L([(18, 14), (28, 14)], INK, 2.25) + L([(18, 21), (34, 21)], INK, 2.25) +
|
||||
L([(18, 28), (31, 28)], INK, 2.25) +
|
||||
C(46, 46, 15, "#FFFFFF", 0, fill="#FFFFFF") +
|
||||
C(46, 46, 12.5, GRN, 0, fill=GRN) +
|
||||
L([(46, 40.5), (46, 51.5)], "#FFFFFF", 3) + L([(40.5, 46), (51.5, 46)], "#FFFFFF", 3))
|
||||
|
||||
# 2 修改项目:完整图纸 + 45° 铅笔(白色隔离层分离,规整几何)
|
||||
def _edit_body():
|
||||
return (
|
||||
P("M10 6 H32 L42 16 V32 H10 Z") +
|
||||
L([(16, 14), (26, 14)], INK, 2.25) + L([(16, 21), (32, 21)], INK, 2.25) +
|
||||
L([(16, 28), (30, 28)], INK, 2.25) +
|
||||
# 铅笔:白色 gap 层(同形放大) → 彩色层。rotate(45) 后笔尖朝左下。
|
||||
'<g transform="translate(45,45) rotate(45)">'
|
||||
+ '<g stroke="#FFFFFF" stroke-width="9" stroke-linejoin="round" fill="#FFFFFF">'
|
||||
'<rect x="-5" y="-17" width="12" height="24" rx="2"/>'
|
||||
'<path d="M-5 7 L1 16.5 L7 7 Z"/></g>'
|
||||
+ '<rect x="-5" y="-17" width="12" height="24" rx="2" fill="#C8791E" stroke="#8A4F0B" stroke-width="2"/>'
|
||||
+ '<path d="M-5 7 L1 16.5 L7 7 Z" fill="#F6D7AC" stroke="#8A4F0B" stroke-width="2"/>'
|
||||
+ '<rect x="-5" y="-17" width="12" height="6" rx="2" fill="#E58E3A" stroke="#8A4F0B" stroke-width="2"/>'
|
||||
+ '</g>')
|
||||
|
||||
ICONS["edit-project"] = S(_edit_body())
|
||||
|
||||
# 3 删除项目:红色垃圾桶
|
||||
ICONS["delete-project"] = S(
|
||||
P("M17 20 H47 L44.3 52 A4 4 0 0 1 40.3 56 H23.7 A4 4 0 0 1 19.7 52 Z", RED, 3) +
|
||||
L([(13, 20), (51, 20)], RED, 3.5) +
|
||||
L([(26, 20), (26, 15)], RED, 3) + L([(38, 20), (38, 15)], RED, 3) +
|
||||
L([(26.5, 28), (27.5, 48)], RED, 2.5) + L([(37.5, 28), (36.5, 48)], RED, 2.5))
|
||||
|
||||
# 4 打开项目:标准两层文件夹(后板墨青线,前板圆角底+淡绿填充,白色 gap 分离)
|
||||
def _open_body():
|
||||
return (
|
||||
P("M8 13 H23 L27.5 18.5 H51 A3.5 3.5 0 0 1 54.5 22 V27 H8 Z") +
|
||||
P("M6.5 31 H57.5 L52.9 46.2 A5.2 5.2 0 0 1 47.9 50.2 H11.1 A5.2 5.2 0 0 1 6.1 45.2 Z",
|
||||
"#FFFFFF", 8, fill="#FFFFFF") +
|
||||
P("M6.5 31 H57.5 L52.9 46.2 A5.2 5.2 0 0 1 47.9 50.2 H11.1 A5.2 5.2 0 0 1 6.1 45.2 Z",
|
||||
GRN, 3, fill=PGR))
|
||||
|
||||
ICONS["open-project"] = S(_open_body())
|
||||
|
||||
# 5 复制项目:双层图纸,后层绿
|
||||
ICONS["copy-project"] = S(
|
||||
P("M22 8 H44 V44 H22 Z") +
|
||||
P("M14 16 V54 H36", GRN, 3) +
|
||||
L([(28, 16), (38, 16)], INK, 2.25) + L([(28, 24), (38, 24)], INK, 2.25) +
|
||||
L([(28, 32), (34, 32)], INK, 2.25))
|
||||
|
||||
# 6 导入项目:托盘 + 绿下箭头
|
||||
ICONS["import-project"] = S(
|
||||
P("M10 34 V48 A6 6 0 0 0 16 54 H48 A6 6 0 0 0 54 48 V34", GRN, 3.5) +
|
||||
L([(32, 6), (32, 26)], GRN, 4) +
|
||||
P("M23 19 L32 28 L41 19", GRN, 4))
|
||||
|
||||
# 7 导出项目:托盘 + 蓝上箭头
|
||||
ICONS["export-project"] = S(
|
||||
P("M10 34 V48 A6 6 0 0 0 16 54 H48 A6 6 0 0 0 54 48 V34", BLU, 3.5) +
|
||||
L([(32, 26), (32, 6)], BLU, 4) +
|
||||
P("M23 13 L32 4 L41 13", BLU, 4))
|
||||
|
||||
# 8 打开项目(报价页):同语义,与项目页"打开项目"同款图形
|
||||
ICONS["open-quote"] = S(_open_body())
|
||||
|
||||
# 9 修改项目(报价页):同语义,与项目页"修改项目"同款图形
|
||||
ICONS["edit-quote"] = S(_edit_body())
|
||||
|
||||
# 10 保存:绿色软盘
|
||||
ICONS["save"] = S(
|
||||
P("M10 10 H44 L54 20 V54 H10 Z", GRN, 3.5) +
|
||||
R(22, 10, 18, 14, 1, GRN, 2.5) +
|
||||
L([(35, 12), (35, 20)], GRN, 2.5) +
|
||||
R(18, 36, 28, 18, 1, GRN, 2.5))
|
||||
|
||||
# 11 导入报价:报价单 + 绿下箭头
|
||||
ICONS["import-quote"] = S(
|
||||
R(10, 8, 30, 44, 2) +
|
||||
L([(17, 18), (33, 18)], INK, 2.25) + L([(17, 25), (29, 25)], INK, 2.25) +
|
||||
T(24, 37, "¥", 12, LGR, "600") +
|
||||
L([(48, 30), (48, 53)], GRN, 4) +
|
||||
P("M40 47 L48 55 L56 47", GRN, 4))
|
||||
|
||||
# 12 甲方报表:表格 + 绿勾角标
|
||||
ICONS["report"] = S(
|
||||
R(8, 10, 48, 44, 3) +
|
||||
L([(8, 22), (56, 22)], INK, 2.5) + L([(8, 33), (56, 33)], INK, 2.5) +
|
||||
L([(24, 22), (24, 54)], INK, 2.5) + L([(40, 22), (40, 54)], INK, 2.5) +
|
||||
C(47, 46, 11.5, fill=GRN) +
|
||||
P("M41.5 46 L45.5 50 L53 41.5", "#FFFFFF", 3.5))
|
||||
|
||||
# 13 调母排:三根铜排(深浅层次) + 调节箭头
|
||||
ICONS["busbar"] = S(
|
||||
R(7, 12, 50, 9, 3, sw=0, fill=COP) +
|
||||
R(7, 28, 50, 9, 3, sw=0, fill="#D69449") +
|
||||
R(7, 44, 38, 9, 3, sw=0, fill="#E5B176") +
|
||||
P("M46 56 L52 50 L58 56", INK, 3))
|
||||
|
||||
# 14 调系数:双滑杆,绿+蓝滑钮
|
||||
ICONS["factor"] = S(
|
||||
L([(10, 21), (54, 21)], INK, 3) + L([(10, 43), (54, 43)], INK, 3) +
|
||||
C(41, 21, 7.5, GRN, 0, fill=GRN) +
|
||||
C(23, 43, 7.5, BLU, 0, fill=BLU))
|
||||
|
||||
# 15 元件排序:三行元件符号(电阻矩形,电气符号) + 上箭头
|
||||
ICONS["sort-components"] = S(
|
||||
L([(9, 15), (19, 15)], INK, 2.5) + R(19, 10.5, 13, 9, 1.5, INK, 2.5) + L([(32, 15), (40, 15)], INK, 2.5) +
|
||||
L([(9, 32), (19, 32)], INK, 2.5) + R(19, 27.5, 13, 9, 1.5, INK, 2.5) + L([(32, 32), (40, 32)], INK, 2.5) +
|
||||
L([(9, 49), (19, 49)], INK, 2.5) + R(19, 44.5, 13, 9, 1.5, INK, 2.5) + L([(32, 49), (40, 49)], INK, 2.5) +
|
||||
P("M50 24 L56 17 L62 24", GRN, 3) +
|
||||
P("M50 44 L56 51 L62 44", GRN, 3))
|
||||
|
||||
# 16 模板费用:模板网格 + ¥ 绿徽
|
||||
ICONS["template-cost"] = S(
|
||||
R(7, 7, 50, 50, 4) +
|
||||
L([(7, 23), (57, 23)], INK, 2.5) + L([(7, 38), (36, 38)], INK, 2.5) +
|
||||
L([(28, 23), (28, 57)], INK, 2.5) +
|
||||
C(47, 46, 12.5, fill=GRN) +
|
||||
T(47, 46.5, "¥", 20))
|
||||
|
||||
# 17 CAD扒图:齿轮 + 绿放大镜
|
||||
ICONS["cad-grab"] = S(
|
||||
C(25, 25, 9.5, INK, 3) +
|
||||
P("M25 7 V13 M25 37 V43 M7 25 H13 M37 25 H43 M12.4 12.4 L16.6 16.6 M33.4 33.4 L37.6 37.6 M37.6 12.4 L33.4 16.6 M16.6 33.4 L12.4 37.6", INK, 4) +
|
||||
C(45, 45, 9.5, GRN, 3.5) +
|
||||
L([(51.5, 51.5), (58, 58)], GRN, 4))
|
||||
|
||||
# 18 登录:门框 + 绿人形
|
||||
ICONS["login"] = S(
|
||||
P("M38 8 H54 V56 H38", INK, 3.5) +
|
||||
C(21, 23, 8, GRN, 3) +
|
||||
P("M7 52 A14 14 0 0 1 35 52", GRN, 3) +
|
||||
C(47, 33, 1.8, INK, 0, fill=INK))
|
||||
|
||||
# 19 修改密码:绿挂锁
|
||||
ICONS["password"] = S(
|
||||
R(14, 28, 36, 27, 4, GRN, 3.5) +
|
||||
P("M22 28 V19 A10 10 0 0 1 42 19 V28", GRN, 3.5) +
|
||||
C(32, 39, 4, GRN, 0, fill=GRN) +
|
||||
L([(32, 43), (32, 49)], GRN, 3))
|
||||
|
||||
# 20 会员信息:证件卡(人像+信息行)
|
||||
ICONS["member"] = S(
|
||||
R(7, 13, 50, 38, 4) +
|
||||
C(23, 29, 6, GRN, 2.75) +
|
||||
P("M13 45 A10 10 0 0 1 33 45", GRN, 2.75) +
|
||||
L([(40, 26), (51, 26)], INK, 2.75) + L([(40, 35), (51, 35)], INK, 2.75) +
|
||||
L([(16, 51), (48, 51)], LGR, 0)) # 占位(卡内底线省略)
|
||||
|
||||
# 21 企业信息:办公楼群(主楼+绿副楼)
|
||||
ICONS["company"] = S(
|
||||
R(11, 9, 27, 47, 1) +
|
||||
L([(18, 19), (24, 19)], INK, 2.5) + L([(30, 19), (36, 19)], INK, 2.5) +
|
||||
L([(18, 28), (24, 28)], INK, 2.5) + L([(30, 28), (36, 28)], INK, 2.5) +
|
||||
L([(18, 37), (24, 37)], INK, 2.5) + L([(30, 37), (36, 37)], INK, 2.5) +
|
||||
R(43, 26, 13, 30, 1, GRN, 3) +
|
||||
L([(48, 34), (52, 34)], GRN, 2.25) + L([(48, 42), (52, 42)], GRN, 2.25) +
|
||||
R(21, 47, 8, 9, 1, INK, 2.5))
|
||||
|
||||
# 22 保存布局:停靠面板示意 + 绿勾
|
||||
ICONS["save-layout"] = S(
|
||||
R(7, 7, 50, 50, 4) +
|
||||
L([(7, 24), (57, 24)], INK, 2.5) + L([(28, 24), (28, 57)], INK, 2.5) +
|
||||
R(11, 11, 13, 9, 1.5, sw=0, fill=LGR) +
|
||||
C(46, 44, 11.5, fill=GRN) +
|
||||
P("M40.5 44 L44.5 48 L52 39.5", "#FFFFFF", 3.5))
|
||||
|
||||
# 23 备份:数据库 + 绿右箭头
|
||||
ICONS["backup"] = S(
|
||||
E(24, 15, 13, 5.5) +
|
||||
P("M11 15 V45 A13 5.5 0 0 0 37 45 V15") +
|
||||
P("M11 30 A13 5.5 0 0 0 37 30", INK, 2.5) +
|
||||
L([(40, 38), (55, 38)], GRN, 3.5) +
|
||||
P("M50 32.5 L56.5 38 L50 43.5", GRN, 3.5))
|
||||
|
||||
# 24 恢复:数据库 + 墨青回转箭头
|
||||
ICONS["restore"] = S(
|
||||
E(40, 15, 13, 5.5, GRN) +
|
||||
P("M27 15 V45 A13 5.5 0 0 0 53 45 V15", GRN) +
|
||||
P("M27 30 A13 5.5 0 0 0 53 30", GRN, 2.5) +
|
||||
L([(24, 38), (9, 38)], INK, 3.5) +
|
||||
P("M14 32.5 L7.5 38 L14 43.5", INK, 3.5))
|
||||
|
||||
|
||||
def render(name, body):
|
||||
"""单个 SVG → Edge 截图 → 64px PNG"""
|
||||
sp = os.path.join(SVG_DIR, name + ".svg")
|
||||
with io.open(sp, "w", encoding="utf-8") as f:
|
||||
f.write(body)
|
||||
tmp = tempfile.mkdtemp(prefix="ribbon_icon_")
|
||||
try:
|
||||
tmp_svg = os.path.join(tmp, "i.svg")
|
||||
tmp_png = os.path.join(tmp, "i.png")
|
||||
shutil.copy(sp, tmp_svg)
|
||||
w = subprocess.run(
|
||||
[EDGE, "--headless", "--disable-gpu", "--no-sandbox",
|
||||
"--user-data-dir=" + os.path.join(tmp, "profile"),
|
||||
"--screenshot=" + tmp_png, "--window-size=256,256",
|
||||
"--default-background-color=00000000",
|
||||
"file:///" + tmp_svg.replace("\\", "/")],
|
||||
capture_output=True, timeout=40)
|
||||
if not os.path.exists(tmp_png):
|
||||
raise RuntimeError("Edge 未产出 PNG: " + w.stderr.decode("utf-8", "ignore")[-150:])
|
||||
img = Image.open(tmp_png).convert("RGBA").resize((64, 64), Image.LANCZOS)
|
||||
img.save(os.path.join(PNG_DIR, name + ".png"))
|
||||
finally:
|
||||
shutil.rmtree(tmp, ignore_errors=True)
|
||||
|
||||
|
||||
def main():
|
||||
os.makedirs(SVG_DIR, exist_ok=True)
|
||||
os.makedirs(PNG_DIR, exist_ok=True)
|
||||
ok, fail = 0, []
|
||||
for name, body in ICONS.items():
|
||||
try:
|
||||
render(name, body)
|
||||
ok += 1
|
||||
except Exception as ex:
|
||||
fail.append((name, str(ex)[:160]))
|
||||
print("SVG→PNG: %d 成功 / %d 失败" % (ok, len(fail)))
|
||||
for n, e in fail:
|
||||
print(" [失败]", n, e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""组装更新包_20261001: 成套报价软件.exe + 使用说明.txt → 目录 + zip"""
|
||||
import os
|
||||
import shutil
|
||||
import zipfile
|
||||
|
||||
ROOT = os.path.normpath(os.path.join(os.path.dirname(os.path.abspath(__file__)), ".."))
|
||||
REL = os.path.join(ROOT, "release", "成套报价软件")
|
||||
OUT = os.path.join(REL, "Output")
|
||||
PKG = os.path.join(OUT, "更新包_20261001_2")
|
||||
|
||||
说明 = """成套报价软件 更新包 2026-10-01 第2期(覆盖式更新)
|
||||
|
||||
本期更新:
|
||||
一、PDF 扒图缩放卡顿修复(重点)
|
||||
原来滚轮缩放每档都在界面线程整页重绘,大幅面图纸一滚就卡死;
|
||||
现在改为后台渲染+旧图即时拉伸过渡——缩放跟手流畅,
|
||||
连续滚动只渲染最后一档,不再反复卡顿。
|
||||
主程序内置查看器与 pdfbt 独立程序已同步修复。
|
||||
二、批量插入修复
|
||||
修复批量插入时报"列'框Handle'不属于表"异常的问题。
|
||||
三、数据落库保障
|
||||
1. 编辑直写队列强制落库改造:关窗/切换项目时数据完整写入数据库;
|
||||
2. 全量重算改为单事务提交,重算期间数据一致性更好。
|
||||
四、查询提速
|
||||
项目库与方案库补齐"内部ID"索引,图号/箱号/元件查询明显提速。
|
||||
五、CAD 扒图窗口修复(与 PDF 扒图同步)
|
||||
1. 右键点击已选中的行不再丢失多选集合;
|
||||
2. F1 合并增加防抖,不再误弹提示。
|
||||
六、稳定性
|
||||
后台异常不再被静默吞掉,关键失败写入日志(安装目录 logs),便于远程排查。
|
||||
|
||||
使用方法:
|
||||
1. 完全关闭成套报价软件(含CAD/PDF扒图也退出);
|
||||
2. 把本包里「成套报价软件.exe」复制到安装目录(默认 C:\\成套报价软件)覆盖;
|
||||
3. 把本包里「pdfbt.exe」复制到安装目录的 pdfbt 子文件夹
|
||||
(默认 C:\\成套报价软件\\pdfbt)覆盖;
|
||||
4. 重新打开软件即可。
|
||||
|
||||
(完整安装包 成套报价软件_Setup.exe 已同步构建,全新机器请用安装包。)
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
if os.path.isdir(PKG):
|
||||
shutil.rmtree(PKG)
|
||||
os.makedirs(PKG)
|
||||
|
||||
# 主程序
|
||||
shutil.copy2(os.path.join(REL, "成套报价软件.exe"), PKG)
|
||||
|
||||
# PDF 扒图独立程序(2026-10-01 第2期:缩放卡顿同步修复)
|
||||
shutil.copy2(os.path.join(REL, "pdfbt", "pdfbt.exe"), PKG)
|
||||
|
||||
# 使用说明(GBK,记事本直接打开不乱码)
|
||||
with open(os.path.join(PKG, "使用说明.txt"), "w", encoding="gbk", newline="\r\n") as f:
|
||||
f.write(说明)
|
||||
|
||||
# zip(文件夹+zip两种形态,与历史更新包一致)
|
||||
zip_path = PKG + ".zip"
|
||||
if os.path.exists(zip_path):
|
||||
os.remove(zip_path)
|
||||
with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as z:
|
||||
for name in os.listdir(PKG):
|
||||
z.write(os.path.join(PKG, name), name)
|
||||
|
||||
print("更新包目录:", PKG)
|
||||
print("更新包zip:", zip_path)
|
||||
for name in os.listdir(PKG):
|
||||
print(" -", name, os.path.getsize(os.path.join(PKG, name)), "字节")
|
||||
print("zip大小:", os.path.getsize(zip_path), "字节")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,322 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
事件/逻辑代码迁移:旧项目 主窗口*.cs + 对话框 → 新项目(停靠面板版)。
|
||||
策略:能复制就复制,只做三类最小改动:
|
||||
1) namespace MyApp → 成套报价软件;partial class 主窗口 → MainForm
|
||||
2) 旧构造函数拆到 MainForm.cs(加 CreateDockWindows/Load 接线)
|
||||
3) 停靠窗口控件在 MainForm 上生成公开别名属性(this.xxx 继续可用)
|
||||
旧 ui.cs 尾部的 SQLite 加载方法一并带过来。
|
||||
"""
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
HERE = os.path.dirname(os.path.abspath(__file__))
|
||||
sys.path.insert(0, HERE)
|
||||
import migrate_ui as mu
|
||||
|
||||
SRC_DIR = os.path.dirname(mu.SRC)
|
||||
DST = mu.DST_PROJECT
|
||||
|
||||
|
||||
def read(p):
|
||||
return io.open(p, encoding="utf-8-sig").read()
|
||||
|
||||
|
||||
def write(p, s):
|
||||
io.open(p, "w", encoding="utf-8-sig", newline="\r\n").write(s)
|
||||
|
||||
|
||||
def rename_common(s):
|
||||
s = s.replace("namespace MyApp", "namespace 成套报价软件")
|
||||
s = s.replace("public partial class 主窗口", "public partial class MainForm")
|
||||
return s
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 1) 别名文件
|
||||
|
||||
def gen_aliases():
|
||||
text = read(mu.SRC)
|
||||
decls, begin_inits, blocks, top, edges, ts_edges = mu.parse(text)
|
||||
ctrl_blocks, form_block = mu.classify(blocks)
|
||||
ch = mu.build_children(edges)
|
||||
decl_types = dict(decls)
|
||||
|
||||
# MainForm 自身已有成员(避免别名冲突)
|
||||
taken = set(["tabControl1", "tabPage1", "tabPage2", "tabPage3", "tabPage4", "tabPage5",
|
||||
"tabPage6", "statusStrip1", "dockSurface1", "components",
|
||||
"InitializeComponent", "CreateDockWindows_dockSurface1", "DockSurface1ActiveContentChanged"])
|
||||
for n, _t in decls:
|
||||
if n in mu.subtree("tabControl1", ch) or n in mu.subtree("statusStrip1", ch):
|
||||
taken.add(n)
|
||||
|
||||
wfields = []
|
||||
props = []
|
||||
for idx, (cls, tab, title) in enumerate(mu.DOCK_MAP, 1):
|
||||
members = mu.subtree(tab, ch)
|
||||
members.remove(tab)
|
||||
mset = set(members)
|
||||
ordered = [n for n, _t in decls if n in mset]
|
||||
for parent, kids in ts_edges:
|
||||
if parent in mset:
|
||||
ordered += [k for k in kids if k not in ordered]
|
||||
wfields.append("\t\tinternal %s w%d;" % (cls, idx))
|
||||
for n in ordered:
|
||||
if n in taken:
|
||||
print("!! 别名冲突跳过:", n)
|
||||
continue
|
||||
props.append("\t\tpublic %s %s { get { return w%d.%s; } }" % (decl_types[n], n, idx, n))
|
||||
taken.add(n)
|
||||
|
||||
L = []
|
||||
a = L.append
|
||||
a("// 旧 主窗口 逻辑代码通过 this.<控件名> 访问控件;")
|
||||
a("// 控件已迁入 9 个停靠窗口,这里用别名属性转发,逻辑代码无需改动。")
|
||||
a("// 停靠窗口实例字段由 MainForm.dockSurface1.DockWindows.cs 在创建后赋值。")
|
||||
a("namespace 成套报价软件")
|
||||
a("{")
|
||||
a("\tpublic partial class MainForm")
|
||||
a("\t{")
|
||||
L.extend(wfields)
|
||||
a("")
|
||||
L.extend(props)
|
||||
a("\t}")
|
||||
a("}")
|
||||
write(os.path.join(DST, "MainForm.控件别名.cs"), "\n".join(L))
|
||||
print("生成 MainForm.控件别名.cs:实例字段 %d 个,别名属性 %d 个" % (len(wfields), len(props)))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 2) 停靠窗口字段改 public
|
||||
|
||||
def publify_fields():
|
||||
pat = re.compile(r"\t\tprivate (?!System\.ComponentModel\.IContainer)([\w.]+) ([\w\u4e00-\u9fff]+);")
|
||||
for cls, _tab, _title in mu.DOCK_MAP:
|
||||
p = os.path.join(DST, "DockWindows", cls, cls + ".Designer.cs")
|
||||
s = read(p)
|
||||
s2, n = pat.subn(r"\t\tpublic \1 \2;", s)
|
||||
write(p, s2)
|
||||
print("%s: %d 个字段改为 public" % (cls, n))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 3) 记录停靠窗口实例
|
||||
|
||||
def patch_dockwindows_cs():
|
||||
p = os.path.join(DST, "DockWindows", "MainForm.dockSurface1.DockWindows.cs")
|
||||
s = read(p)
|
||||
n = 0
|
||||
for idx, (cls, _tab, _title) in enumerate(mu.DOCK_MAP, 1):
|
||||
old = "var w%d = new %s();" % (idx, cls)
|
||||
new = old + "\n\t\t\tthis.w%d = w%d;" % (idx, idx)
|
||||
if old in s and new not in s:
|
||||
s = s.replace(old, new)
|
||||
n += 1
|
||||
write(p, s)
|
||||
print("MainForm.dockSurface1.DockWindows.cs: 接线 %d 处" % n)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 4) MainForm.cs + 主窗口.cs
|
||||
|
||||
def split_main():
|
||||
src = read(os.path.join(SRC_DIR, "主窗口.cs"))
|
||||
lines = src.split("\n")
|
||||
ctor_start = None
|
||||
for i, l in enumerate(lines):
|
||||
if re.match(r"\s*public 主窗口\(\)", l):
|
||||
ctor_start = i
|
||||
break
|
||||
assert ctor_start is not None, "找不到旧构造函数"
|
||||
# ctor 结束:InitSelectionPage(); 后的第一个 ' }'
|
||||
ctor_end = None
|
||||
for i in range(ctor_start, len(lines)):
|
||||
if "InitSelectionPage();" in lines[i]:
|
||||
for j in range(i, min(i + 5, len(lines))):
|
||||
if lines[j].strip() == "}":
|
||||
ctor_end = j
|
||||
break
|
||||
break
|
||||
assert ctor_end is not None, "找不到构造函数结束"
|
||||
|
||||
usings = [l for l in lines[:ctor_start] if l.startswith("using ")]
|
||||
ctor_body = lines[ctor_start + 3:ctor_end] # 跳过签名/{/InitializeComponent();
|
||||
|
||||
# 新 MainForm.cs
|
||||
L = []
|
||||
a = L.append
|
||||
a("/*")
|
||||
a(" * 由SharpDevelop创建。")
|
||||
a(" * 用户: 099978")
|
||||
a(" * 日期: 2026-08-25")
|
||||
a(" *")
|
||||
a(" * 主窗体构造函数(由旧项目 主窗口.cs 迁移)。")
|
||||
a(" */")
|
||||
L.extend(usings)
|
||||
a("")
|
||||
a("namespace 成套报价软件")
|
||||
a("{")
|
||||
a("\t/// <summary>")
|
||||
a("\t/// 主窗体:构造函数迁移自旧项目 主窗口.cs(事件挂接全部保留)。")
|
||||
a("\t/// </summary>")
|
||||
a("\tpublic partial class MainForm : Form")
|
||||
a("\t{")
|
||||
a("\t\tpublic MainForm()")
|
||||
a("\t\t{")
|
||||
a("\t\t\tInitializeComponent();")
|
||||
a("\t\t\tCreateDockWindows_dockSurface1();")
|
||||
a("")
|
||||
a("\t\t\t// 旧 ui.cs 设计器注入的 SQLite 数据加载(Load 事件)")
|
||||
a("\t\t\tthis.Load += (s, e) => LoadAllSqliteData();")
|
||||
a("")
|
||||
L.extend(ctor_body)
|
||||
a("")
|
||||
a("\t\tvoid DockSurface1ActiveContentChanged(object sender, EventArgs e)")
|
||||
a("\t\t{")
|
||||
a("")
|
||||
a("\t\t}")
|
||||
a("\t}")
|
||||
a("}")
|
||||
write(os.path.join(DST, "MainForm.cs"), "\n".join(L))
|
||||
|
||||
# 主窗口.cs(去掉构造函数的其余逻辑)
|
||||
rest = lines[:ctor_start] + [" // 构造函数已迁至 MainForm.cs", ""] + lines[ctor_end + 1:]
|
||||
write(os.path.join(DST, "主窗口.cs"), rename_common("\n".join(rest)))
|
||||
print("生成 MainForm.cs 与 主窗口.cs(构造函数 %d-%d 行)" % (ctor_start + 1, ctor_end + 1))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 5) 其余 partial 与工具类
|
||||
|
||||
def copy_partials():
|
||||
files = ["主窗口.菜单与库.cs", "主窗口.公式重算.cs", "主窗口.母排与备份.cs", "主窗口.项目导入导出.cs", "价格格式化.cs"]
|
||||
for f in files:
|
||||
write(os.path.join(DST, f), rename_common(read(os.path.join(SRC_DIR, f))))
|
||||
print("复制", f)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 6) ui.cs 尾部 SQLite 加载
|
||||
|
||||
def port_sqlite_tail():
|
||||
lines = read(mu.SRC).split("\n")
|
||||
start = None
|
||||
for i, l in enumerate(lines):
|
||||
if "加载所有 SQLite 设计数据绑定" in l:
|
||||
start = i
|
||||
break
|
||||
assert start is not None
|
||||
tail = lines[start:]
|
||||
while tail and tail[-1].strip() in ("}", ""):
|
||||
tail.pop()
|
||||
L = ["// 旧 主窗口.ui.cs 尾部的 SQLite 设计数据加载方法(设计器注入,逻辑不变)。"]
|
||||
L.append("namespace 成套报价软件")
|
||||
L.append("{")
|
||||
L.append("\tpublic partial class MainForm")
|
||||
L.append("\t{")
|
||||
L.extend(tail)
|
||||
L.append("\t}")
|
||||
L.append("}")
|
||||
write(os.path.join(DST, "主窗口.SQLite加载.cs"), "\n".join(L))
|
||||
print("生成 主窗口.SQLite加载.cs(%d 行)" % len(tail))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 7) 对话框
|
||||
|
||||
DIALOGS = [
|
||||
("xiangmu", ["新建项目", "修改项目", "甲方报表", "调系数", "导入报价", "元件排序", "调母排"]),
|
||||
("shezhi", ["企业信息"]),
|
||||
("ku", ["用户库元件编辑"]),
|
||||
("GCS母排计算", ["GCS母排计算窗口", "GCS推荐铜排窗口"]),
|
||||
]
|
||||
|
||||
|
||||
def copy_dialogs():
|
||||
for sub, names in DIALOGS:
|
||||
for name in names:
|
||||
for ext in (".cs", ".ui.cs"):
|
||||
s = read(os.path.join(SRC_DIR, sub, name + ext))
|
||||
s = rename_common(s)
|
||||
# 类型引用 主窗口 → MainForm(注释里的中文连写不受影响)
|
||||
s = re.sub(r"主窗口(?=[\s\.\)\[_,;])", "MainForm", s)
|
||||
dst_sub = os.path.join(DST, sub)
|
||||
if not os.path.isdir(dst_sub):
|
||||
os.makedirs(dst_sub)
|
||||
write(os.path.join(dst_sub, name + ext), s)
|
||||
print("复制", sub + "\\" + name + ext)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 8) csproj
|
||||
|
||||
CSPROJ_COMPILE = [
|
||||
"主窗口.cs", "主窗口.菜单与库.cs", "主窗口.公式重算.cs", "主窗口.母排与备份.cs",
|
||||
"主窗口.项目导入导出.cs", "主窗口.SQLite加载.cs", "MainForm.控件别名.cs", "价格格式化.cs",
|
||||
]
|
||||
for sub, names in DIALOGS:
|
||||
for name in names:
|
||||
CSPROJ_COMPILE.append(sub + "\\" + name + ".cs")
|
||||
CSPROJ_COMPILE.append(sub + "\\" + name + ".ui.cs")
|
||||
|
||||
LIB_REFS = [
|
||||
"System.Data.SQLite", "NPOI", "NPOI.OOXML", "NPOI.OpenXml4Net", "NPOI.OpenXmlFormats",
|
||||
"DotNetZip", "ICSharpCode.SharpZipLib", "unvell.ReoGrid", "CJZZ.Controls",
|
||||
]
|
||||
|
||||
|
||||
def patch_csproj():
|
||||
p = os.path.join(DST, "成套报价软件.csproj")
|
||||
s = read(p)
|
||||
s = s.replace("<PlatformTarget>x86</PlatformTarget>", "<PlatformTarget>x64</PlatformTarget>")
|
||||
# System.Data.SQLite 从 SD bin(x86) 改为 lib\x64 版
|
||||
s = re.sub(
|
||||
r"<Reference Include=\"System\.Data\.SQLite\">\s*<HintPath>.*?</HintPath>\s*</Reference>",
|
||||
lambda m: '<Reference Include="System.Data.SQLite">\n <HintPath>lib\\System.Data.SQLite.dll</HintPath>\n <Private>True</Private>\n </Reference>',
|
||||
s, flags=re.S)
|
||||
# 追加 lib 引用(插到 WeifenLuo.ThemeVS2015 引用块之后)
|
||||
ref_xml = ""
|
||||
for name in LIB_REFS:
|
||||
ref_xml += ' <Reference Include="%s">\n <HintPath>lib\\%s.dll</HintPath>\n <Private>True</Private>\n </Reference>\n' % (name, name)
|
||||
ref_xml += ' <Reference Include="System.IO.Compression" />\n <Reference Include="System.IO.Compression.FileSystem" />\n'
|
||||
anchor = "</Reference>\n </ItemGroup>"
|
||||
idx = s.rfind(anchor)
|
||||
s = s[:idx + len("</Reference>\n")] + ref_xml + " </ItemGroup>" + s[idx + len(anchor):]
|
||||
# 追加 Compile 条目
|
||||
comp_xml = ""
|
||||
for f in CSPROJ_COMPILE:
|
||||
comp_xml += ' <Compile Include="%s" />\n' % f
|
||||
anchor2 = ' <Compile Include="Program.cs" />'
|
||||
s = s.replace(anchor2, comp_xml + anchor2)
|
||||
write(p, s)
|
||||
print("csproj 已更新(x64 + %d 引用 + %d 编译项)" % (len(LIB_REFS) + 2, len(CSPROJ_COMPILE)))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 9) verify_build.rsp
|
||||
|
||||
def gen_rsp():
|
||||
L = ["# csc 编译验证(UTF-8)", "/target:winexe", "/platform:x64", "/utf8output", "/nologo", "/warn:0",
|
||||
"/out:bin\\Debug\\成套报价软件.exe"]
|
||||
for fw in ["System.dll", "System.Core.dll", "System.Data.dll", "System.Drawing.dll",
|
||||
"System.Windows.Forms.dll", "System.Xml.dll", "System.IO.Compression.dll",
|
||||
"System.IO.Compression.FileSystem.dll"]:
|
||||
L.append("/r:" + fw)
|
||||
for name in ["Smart.CustomComponents", "WeifenLuo.WinFormsUI.Docking", "WeifenLuo.WinFormsUI.Docking.ThemeVS2015"]:
|
||||
L.append('/r:"C:\\Program Files (x86)\\SharpDevelop\\5.2\\bin\\%s.dll"' % name)
|
||||
for name in ["AdvancedDataGridView", "Microsoft.Web.WebView2.Core", "Microsoft.Web.WebView2.WinForms"] + LIB_REFS:
|
||||
L.append('/r:"lib\\%s.dll"' % name)
|
||||
for f in ["MainForm.cs", "MainForm.Designer.cs", "Program.cs", "Properties\\AssemblyInfo.cs"] + CSPROJ_COMPILE:
|
||||
L.append(f)
|
||||
for cls, _t, _ti in mu.DOCK_MAP:
|
||||
L.append("DockWindows\\%s\\%s.cs" % (cls, cls))
|
||||
L.append("DockWindows\\%s\\%s.Designer.cs" % (cls, cls))
|
||||
L.append("DockWindows\\MainForm.dockSurface1.DockWindows.cs")
|
||||
write(os.path.join(HERE, "verify_build.rsp"), "\n".join(L) + "\n")
|
||||
print("verify_build.rsp 已重新生成")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
gen_aliases()
|
||||
publify_fields()
|
||||
patch_dockwindows_cs()
|
||||
split_main()
|
||||
copy_partials()
|
||||
port_sqlite_tail()
|
||||
copy_dialogs()
|
||||
patch_csproj()
|
||||
gen_rsp()
|
||||
print("完成")
|
||||
@@ -0,0 +1,152 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""一次性迁移:把 python 代理的 proxy_cache 展开成静态目录树 → bin\\Debug\\元件库网页\\
|
||||
数据平移,不带旧服务逻辑。规则:
|
||||
1. price_standalone.html / jquery.min.js 直接拷到根
|
||||
2. 关键资源按【页面引用清单 override】精确落位(解决键名下划线歧义)
|
||||
3. electricData/privateJson/electricPic/privateImg 各取最大缓存 → 固定路径(查询忽略)
|
||||
4. webGetPrice 族(dx2_pcprice..._hash) → dx2_webGetPrice\\ 原键保存(server.cjs 按 infoJson hash 查)
|
||||
5. 其余键:下划线全展开为目录树,同类多版本保留最新
|
||||
"""
|
||||
import os, re, shutil, sys
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
SRC = os.path.join(ROOT, "旧服务-python代理", "src")
|
||||
CACHE = os.path.join(SRC, "proxy_cache")
|
||||
PAGE_DIR = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "元件库网页")
|
||||
OUT = os.path.join(PAGE_DIR, "data") # 迁移只重建 data\;server.cjs/页面 在外层不动
|
||||
|
||||
# 键名 → 精确目标路径(页面/SDK 实际请求;键不带 .data 后缀)
|
||||
OVERRIDE = {
|
||||
"dian_open_api_css_jprice_public.min.css": "dian/open/api/css/jprice_public.min.css",
|
||||
"open_api_css_jprice_public.min.css": "open/api/css/jprice_public.min.css",
|
||||
"open_api_css_jprice.min.css": "open/api/css/jprice.min.css",
|
||||
"dian_open_api_js_jprice-2.0.0.js": "dian/open/api/js/jprice-2.0.0.js",
|
||||
"viewer2_preview_ztree_zTreeStyle.css": "viewer2/preview/ztree/zTreeStyle.css",
|
||||
"viewer2_preview_ztree_preview.css": "viewer2/preview/ztree/preview.css",
|
||||
"open_api_images_zTreeStandard.png": "open/api/images/zTreeStandard.png",
|
||||
"images_nopic.png": "images/nopic.png",
|
||||
}
|
||||
|
||||
def main():
|
||||
if not os.path.isdir(CACHE):
|
||||
print("!! 找不到 proxy_cache:", CACHE); sys.exit(1)
|
||||
if os.path.isdir(OUT):
|
||||
shutil.rmtree(OUT)
|
||||
os.makedirs(OUT)
|
||||
|
||||
# 1. 页面与 jquery(放外层,与 server.cjs 同级)
|
||||
for f in ("price_standalone.html", "jquery.min.js"):
|
||||
shutil.copy2(os.path.join(SRC, f), os.path.join(PAGE_DIR, f))
|
||||
print("OK 页面+jquery")
|
||||
|
||||
# 收集缓存键(去 .meta/.data,同名保留最新 mtime 的 .data)
|
||||
entries = {} # 键 -> (data路径, mtime, size)
|
||||
for name in os.listdir(CACHE):
|
||||
if not name.endswith(".data"):
|
||||
continue
|
||||
key = name[:-5]
|
||||
p = os.path.join(CACHE, name)
|
||||
st = os.stat(p)
|
||||
old = entries.get(key)
|
||||
if old is None or st.st_mtime > old[1]:
|
||||
entries[key] = (p, st.st_mtime, st.st_size)
|
||||
print("缓存键总数:", len(entries))
|
||||
|
||||
# 2. webGetPrice 族 → 独立目录原键保存
|
||||
n_wgp = 0
|
||||
os.makedirs(os.path.join(OUT, "dx2_webGetPrice"), exist_ok=True)
|
||||
for key, (p, _, _) in list(entries.items()):
|
||||
if "webGetPrice" in key:
|
||||
shutil.copy2(p, os.path.join(OUT, "dx2_webGetPrice", key + ".data"))
|
||||
entries.pop(key); n_wgp += 1
|
||||
print("OK webGetPrice:", n_wgp)
|
||||
|
||||
# 3. electricData 族:全部 ZIP 按内部文件名平移到 electricData_zips\(server.cjs 按 v 参数路由)
|
||||
import zipfile
|
||||
n_zip = 0
|
||||
os.makedirs(os.path.join(OUT, "dian", "electricData_zips"), exist_ok=True)
|
||||
for key in list(entries.keys()):
|
||||
if "electricData" not in key:
|
||||
continue
|
||||
p = entries[key][0]
|
||||
try:
|
||||
z = zipfile.ZipFile(p)
|
||||
inner = z.namelist()[0] if z.namelist() else ""
|
||||
except Exception:
|
||||
inner = ""
|
||||
if not inner:
|
||||
continue
|
||||
base = inner[:-3] if inner.endswith(".js") else (inner[:-2] if inner.endswith("js") else inner)
|
||||
base = base.rstrip(".")
|
||||
dst = os.path.join(OUT, "dian", "electricData_zips", base + ".zip")
|
||||
if not os.path.exists(dst):
|
||||
shutil.copy2(p, dst)
|
||||
n_zip += 1
|
||||
entries.pop(key)
|
||||
print("OK electricData ZIPs:", n_zip)
|
||||
|
||||
# electricPic 等取最大一份 → 固定路径
|
||||
for 词, 目标 in [("privateJson", ("dian", "privateJson")),
|
||||
("electricPic", ("dian", "electricPic")),
|
||||
("privateImg", ("dian", "privateImg"))]:
|
||||
best_key, best = None, None
|
||||
for key, (p, mt, sz) in entries.items():
|
||||
base = key.rsplit("/", 1)[-1]
|
||||
名 = base.split("_")[0] if "_" in base else base
|
||||
# 键形如 dian_electricData_xxxxxxxx / electricData
|
||||
parts = key.split("_")
|
||||
if 词 in parts or key == 词 or (parts and parts[0] == "dian" and len(parts) > 1 and parts[1].startswith(词)):
|
||||
if best is None or sz > best[2]:
|
||||
best_key, best = key, (p, mt, sz)
|
||||
if best:
|
||||
dst = os.path.join(OUT, *目标)
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
shutil.copy2(best[0], dst)
|
||||
entries.pop(best_key)
|
||||
print("OK %s <- %s (%d B)" % (词, best_key, best[2]))
|
||||
else:
|
||||
print("!! %s 无缓存" % 词)
|
||||
|
||||
# 4. override 精确落位
|
||||
n_ov = 0
|
||||
for key, rel in OVERRIDE.items():
|
||||
if key in entries:
|
||||
dst = os.path.join(OUT, *rel.split("/"))
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
shutil.copy2(entries[key][0], dst)
|
||||
entries.pop(key); n_ov += 1
|
||||
else:
|
||||
# 有参数hash变体:找 键_hash
|
||||
cand = [k for k in entries if k == key or k.startswith(key + "_")]
|
||||
if cand:
|
||||
cand.sort(key=lambda k: -entries[k][2])
|
||||
k0 = cand[0]
|
||||
dst = os.path.join(OUT, *rel.split("/"))
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
shutil.copy2(entries[k0][0], dst)
|
||||
entries.pop(k0); n_ov += 1
|
||||
else:
|
||||
print("!! override 缺缓存:", key)
|
||||
print("OK override:", n_ov)
|
||||
|
||||
# 5. 其余全展开(下划线→斜杠);去掉 _参数hash 尾巴后仍带 8 位 hex 的整段保留
|
||||
n_tree = 0
|
||||
for key, (p, _, _) in entries.items():
|
||||
rel = key.replace("_", "/")
|
||||
dst = os.path.join(OUT, *rel.split("/"))
|
||||
try:
|
||||
os.makedirs(os.path.dirname(dst), exist_ok=True)
|
||||
if os.path.exists(dst):
|
||||
os.remove(dst)
|
||||
shutil.copy2(p, dst)
|
||||
n_tree += 1
|
||||
except OSError:
|
||||
pass # 非法文件名等跳过
|
||||
print("OK 目录树:", n_tree)
|
||||
|
||||
total = sum(len(fs) for _, _, fs in os.walk(OUT))
|
||||
size = sum(os.path.getsize(os.path.join(r, f)) for r, _, fs in os.walk(OUT) for f in fs)
|
||||
print("完成: %d 文件, %.1f MB → %s" % (total, size / 1048576.0, OUT))
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,777 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
把旧项目 主窗口.ui.cs 的 UI 迁移到 SharpDevelop 停靠面板项目。
|
||||
映射:旧 tabControl2/tabControl3 的 9 个页签 → 9 个 DockWindow;
|
||||
旧 tabControl1(顶部 项目/报价/设置)+ statusStrip1 → MainForm。
|
||||
仅迁移 UI(控件、属性、布局),事件与逻辑下一阶段迁移。
|
||||
"""
|
||||
import io
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
SRC = r"F:\pycode\wpf\src\WinFormsDesigner.Fx48\bin\Debug\net48\Samples\COOO\scr\成套报价软件\主窗口.ui.cs"
|
||||
DST_PROJECT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
|
||||
|
||||
DOCK_MAP = [
|
||||
("DockWindow", "tabPage3", "项目"),
|
||||
("DockWindow2", "tabPage4", "报价"),
|
||||
("DockWindow3", "tabPage10", "选型"),
|
||||
("DockWindow4", "tabPage5", "元件库"),
|
||||
("DockWindow5", "tabPage6", "用户库"),
|
||||
("DockWindow6", "tabPage11", "柜体库"),
|
||||
("DockWindow7", "tabPage12", "母排"),
|
||||
("DockWindow8", "tabPage13", "方案"),
|
||||
("DockWindow9", "tabPage14", "箱体计算"),
|
||||
]
|
||||
|
||||
NAME = r"[A-Za-z0-9_\u4e00-\u9fff]+"
|
||||
|
||||
# ---------------------------------------------------------------- 解析
|
||||
|
||||
def parse(text):
|
||||
lines = text.split("\n")
|
||||
decls = [] # [(name, fulltype)] 按旧文件声明顺序
|
||||
decls_seen = set()
|
||||
begin_inits = set()
|
||||
top_statements = [] # 声明区/尾部区语句(无块头上下文)
|
||||
blocks = [] # [(header_name, [statement strings])] 属性块
|
||||
edges = [] # [(parent_key, [child])] Controls.AddRange
|
||||
ts_edges = [] # [(toolstrip, [items])] ToolStrip Items.AddRange
|
||||
cur_name = None
|
||||
cur_stmt = []
|
||||
cur_lines = []
|
||||
prev_blank_comment = False
|
||||
|
||||
def flush_stmt():
|
||||
nonlocal cur_stmt
|
||||
if not cur_stmt:
|
||||
return
|
||||
stmt = "\n".join(cur_stmt).strip()
|
||||
cur_stmt = []
|
||||
if not stmt:
|
||||
return
|
||||
m = re.match(r"this\.(" + NAME + r"(?:\.Panel[12])?)\.Controls\.AddRange\(new System\.Windows\.Forms\.Control\[\] \{(.*?)\}\);", stmt, re.S)
|
||||
if m:
|
||||
kids = re.findall(r"this\.(" + NAME + r")(?=[,\s}])", m.group(2))
|
||||
edges.append((m.group(1), kids))
|
||||
return
|
||||
m = re.match(r"this\.Controls\.AddRange\(new System\.Windows\.Forms\.Control\[\] \{(.*?)\}\);", stmt, re.S)
|
||||
if m:
|
||||
kids = re.findall(r"this\.(" + NAME + r")(?=[,\s}])", m.group(1))
|
||||
edges.append(("", kids))
|
||||
return
|
||||
m = re.match(r"this\.(" + NAME + r")\.Items\.AddRange\(new System\.Windows\.Forms\.ToolStripItem\[\] \{(.*?)\}\);", stmt, re.S)
|
||||
if m:
|
||||
kids = re.findall(r"this\.(" + NAME + r")(?=[,\s}])", m.group(2))
|
||||
ts_edges.append((m.group(1), kids))
|
||||
return
|
||||
m = re.match(r"this\.(" + NAME + r") = new ([\w.]+)\(\);$", stmt)
|
||||
if m:
|
||||
if m.group(1) == "components":
|
||||
return
|
||||
if m.group(1) not in decls_seen:
|
||||
decls.append((m.group(1), m.group(2)))
|
||||
decls_seen.add(m.group(1))
|
||||
return
|
||||
if re.match(r"\(\(System\.ComponentModel\.ISupportInitialize\)\(this\.(" + NAME + r")\)\)\.BeginInit\(\);", stmt):
|
||||
begin_inits.add(re.match(r"\(\(System\.ComponentModel\.ISupportInitialize\)\(this\.(" + NAME + r")\)\)\.BeginInit\(\);", stmt).group(1))
|
||||
return
|
||||
if cur_name is not None:
|
||||
cur_lines.append(stmt)
|
||||
else:
|
||||
top_statements.append(stmt)
|
||||
|
||||
field_re = re.compile(r"^private [\w.]+ " + NAME + r";$")
|
||||
for raw in lines:
|
||||
line = raw.rstrip("\r")
|
||||
s = line.strip()
|
||||
if field_re.match(s):
|
||||
continue
|
||||
m = re.match(r"^ {12}// (" + NAME + r")$", line)
|
||||
if m and prev_blank_comment:
|
||||
flush_stmt()
|
||||
if cur_name is not None and cur_lines:
|
||||
blocks.append((cur_name, cur_lines))
|
||||
cur_name, cur_lines = m.group(1), []
|
||||
prev_blank_comment = False
|
||||
continue
|
||||
prev_blank_comment = (s == "//")
|
||||
if s.startswith("this.") or s.startswith("((System") or cur_stmt:
|
||||
cur_stmt.append(s)
|
||||
if s.endswith(";"):
|
||||
flush_stmt()
|
||||
flush_stmt()
|
||||
if cur_name is not None and cur_lines:
|
||||
blocks.append((cur_name, cur_lines))
|
||||
return decls, begin_inits, blocks, top_statements, edges, ts_edges
|
||||
|
||||
|
||||
def classify(blocks):
|
||||
ctrl_blocks = {}
|
||||
form_block = []
|
||||
for name, stmts in blocks:
|
||||
if name == "主窗口":
|
||||
form_block = stmts
|
||||
else:
|
||||
ctrl_blocks[name] = stmts
|
||||
return ctrl_blocks, form_block
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 默认值过滤
|
||||
|
||||
DROP_GLOBAL = [
|
||||
r"\.Enabled = true;$",
|
||||
r"\.Visible = true;$",
|
||||
r"\.TabStop = true;$",
|
||||
r"\.Font = new System\.Drawing\.Font\(\"宋体\", 9F, System\.Drawing\.FontStyle\.Regular, System\.Drawing\.GraphicsUnit\.Point(, 1)?\);$",
|
||||
r"\.ForeColor = System\.Drawing\.SystemColors\.ControlText;$",
|
||||
r"\.Margin = new System\.Windows\.Forms\.Padding\((3, 3, 3, 3|0, 0, 0, 0)\);$",
|
||||
r"\.MaximumSize = new System\.Drawing\.Size\(0, 0\);$",
|
||||
r"\.MinimumSize = new System\.Drawing\.Size\(0, 0\);$",
|
||||
r"\.Padding = new System\.Windows\.Forms\.Padding\(0, 0, 0, 0\);$",
|
||||
r"\.AutoScroll = false;$",
|
||||
r"\.AutoSize = false;$",
|
||||
r"\.AutoSizeMode = System\.Windows\.Forms\.AutoSizeMode\.GrowOnly;$",
|
||||
r"\.AutoValidate = System\.Windows\.Forms\.AutoValidate\.\w+;$",
|
||||
r"\.AllowDrop = false;$",
|
||||
r"\.UseWaitCursor = false;$",
|
||||
r"\.BackgroundImageLayout = System\.Windows\.Forms\.ImageLayout\.Tile;$",
|
||||
r"\.AutoEllipsis = false;$",
|
||||
r"\.DialogResult = System\.Windows\.Forms\.DialogResult\.None;$",
|
||||
r"\.FlatStyle = System\.Windows\.Forms\.FlatStyle\.Standard;$",
|
||||
r"\.ImageAlign = System\.Drawing\.ContentAlignment\.MiddleCenter;$",
|
||||
r"\.TextImageRelation = System\.Windows\.Forms\.TextImageRelation\.Overlay;$",
|
||||
r"\.UseMnemonic = true;$",
|
||||
r"\.UseCompatibleTextRendering = false;$",
|
||||
r"\.Cursor = System\.Windows\.Forms\.Cursors\.Default;$",
|
||||
r"\.ImeMode = System\.Windows\.Forms\.ImeMode\.\w+;$",
|
||||
r"\.RightToLeft = System\.Windows\.Forms\.RightToLeft\.No;$",
|
||||
r"\.CauseValidation = true;$",
|
||||
r"\.Dock = System\.Windows\.Forms\.DockStyle\.None;$",
|
||||
r"\.GridColor = System\.Drawing\.SystemColors\.ControlDark;$",
|
||||
r"\.ColumnHeadersVisible = true;$",
|
||||
r"\.RowHeadersVisible = true;$",
|
||||
r"\.ShowCellErrors = true;$",
|
||||
r"\.ShowCellToolTips = true;$",
|
||||
r"\.ShowEditingIcon = true;$",
|
||||
r"\.ShowRowErrors = true;$",
|
||||
r"\.StandardTab = false;$",
|
||||
r"\.VirtualMode = false;$",
|
||||
r"\.MultiSelect = true;$",
|
||||
r"\.ScrollBars = System\.Windows\.Forms\.ScrollBars\.Both;$",
|
||||
r"\.AllowUserToResizeColumns = true;$",
|
||||
r"\.AutoSizeColumnsMode = System\.Windows\.Forms\.DataGridViewAutoSizeColumnsMode\.None;$",
|
||||
r"\.AutoSizeRowsMode = System\.Windows\.Forms\.DataGridViewAutoSizeRowsMode\.None;$",
|
||||
r"\.ClipboardCopyMode = System\.Windows\.Forms\.DataGridViewClipboardCopyMode\.EnableWithAutoHeaderText;$",
|
||||
r"\.ColumnHeadersBorderStyle = System\.Windows\.Forms\.DataGridViewHeaderBorderStyle\.Raised;$",
|
||||
r"\.RowHeadersBorderStyle = System\.Windows\.Forms\.DataGridViewHeaderBorderStyle\.Raised;$",
|
||||
r"\.RowHeadersWidthSizeMode = System\.Windows\.Forms\.DataGridViewRowHeadersWidthSizeMode\.EnableResizing;$",
|
||||
r"\.ColumnHeadersHeightSizeMode = System\.Windows\.Forms.DataGridViewColumnHeadersHeightSizeMode\.EnableResizing;$",
|
||||
r"\.EnableCellPaste = false;$",
|
||||
r"\.DefaultCellStyle\.BackColor = System\.Drawing\.SystemColors\.Window;$",
|
||||
r"\.DefaultCellStyle\.ForeColor = System\.Drawing\.SystemColors\.ControlText;$",
|
||||
r"\.DefaultCellStyle\.SelectionBackColor = System\.Drawing\.SystemColors\.Highlight;$",
|
||||
r"\.DefaultCellStyle\.SelectionForeColor = System\.Drawing\.SystemColors\.HighlightText;$",
|
||||
r"\.DefaultCellStyle\.WrapMode = System\.Windows\.Forms\.DataGridViewTriState\.False;$",
|
||||
r"\.DefaultCellStyle\.Alignment = System\.Windows\.Forms\.DataGridViewContentAlignment\.MiddleLeft;$",
|
||||
r"\.(ColumnHeadersDefaultCellStyle|RowHeadersDefaultCellStyle|DefaultCellStyle)\.Font = new System\.Drawing\.Font\(\"宋体\", 9F.*\);$",
|
||||
r"\.(ColumnHeadersDefaultCellStyle|RowHeadersDefaultCellStyle)\.BackColor = System\.Drawing\.SystemColors\.Control;$",
|
||||
r"\.(ColumnHeadersDefaultCellStyle|RowHeadersDefaultCellStyle)\.ForeColor = System\.Drawing\.SystemColors\.WindowText;$",
|
||||
r"\.(ColumnHeadersDefaultCellStyle|RowHeadersDefaultCellStyle)\.SelectionBackColor = System\.Drawing\.SystemColors\.Highlight;$",
|
||||
r"\.(ColumnHeadersDefaultCellStyle|RowHeadersDefaultCellStyle)\.SelectionForeColor = System\.Drawing\.SystemColors\.HighlightText;$",
|
||||
r"\.(ColumnHeadersDefaultCellStyle|RowHeadersDefaultCellStyle)\.WrapMode = System\.Windows\.Forms\.DataGridViewTriState\.True;$",
|
||||
r"\.(ColumnHeadersDefaultCellStyle|RowHeadersDefaultCellStyle)\.Alignment = System\.Windows\.Forms\.DataGridViewContentAlignment\.MiddleLeft;$",
|
||||
# TextBox / ComboBox 默认值
|
||||
r"\.AcceptsReturn = false;$",
|
||||
r"\.AutoCompleteMode = System\.Windows\.Forms\.AutoCompleteMode\.None;$",
|
||||
r"\.AutoCompleteSource = System\.Windows\.Forms\.AutoCompleteSource\.None;$",
|
||||
r"\.CharacterCasing = System\.Windows\.Forms\.CharacterCasing\.Normal;$",
|
||||
r"\.Multiline = false;$",
|
||||
r"\.UseSystemPasswordChar = false;$",
|
||||
r"\.AcceptsTab = false;$",
|
||||
r"\.ShortcutsEnabled = true;$",
|
||||
r"\.HideSelection = true;$",
|
||||
r"\.WordWrap = true;$",
|
||||
r"\.MaxLength = 32767;$",
|
||||
r"\.MaxLength = 0;$",
|
||||
r"\.ReadOnly = false;$",
|
||||
r"\.ScrollBars = System\.Windows\.Forms\.ScrollBars\.None;$",
|
||||
r"\.TextAlign = System\.Windows\.Forms\.HorizontalAlignment\.Left;$",
|
||||
r"\.BorderStyle = System\.Windows\.Forms\.BorderStyle\.Fixed3D;$",
|
||||
r"\.DrawMode = System\.Windows\.Forms\.DrawMode\.Normal;$",
|
||||
r"\.IntegralHeight = true;$",
|
||||
r"\.MaxDropDownItems = 8;$",
|
||||
r"\.Sorted = false;$",
|
||||
r"\.DropDownStyle = System\.Windows\.Forms\.ComboBoxStyle\.DropDown;$",
|
||||
r"\.CanOverflow = true;$",
|
||||
r"\.AllowItemReorder = false;$",
|
||||
r"\.AllowMerge = true;$",
|
||||
r"\.GripMargin = new System\.Windows\.Forms\.Padding\(2, 2, 2, 2\);$",
|
||||
r"\.ImageScalingSize = new System\.Drawing\.Size\(16, 16\);$",
|
||||
r"\.LayoutStyle = System\.Windows\.Forms\.ToolStripLayoutStyle\.HorizontalStackWithOverflow;$",
|
||||
r"\.Stretch = false;$",
|
||||
r"\.RenderMode = System\.Windows\.Forms\.ToolStripRenderMode\.ManagerRenderMode;$",
|
||||
r"\.ShowItemToolTips = true;$",
|
||||
r"\.TextDirection = System\.Windows\.Forms\.ToolStripTextDirection\.Horizontal;$",
|
||||
r"\.Padding = new System\.Windows\.Forms\.Padding\(0, 0, 1, 0\);$",
|
||||
]
|
||||
|
||||
DROP_BY_TYPE = {
|
||||
"Panel": [r"\.BorderStyle = System\.Windows\.Forms\.BorderStyle\.None;$",
|
||||
r"\.BackColor = System\.Drawing\.SystemColors\.Control;$"],
|
||||
"SplitContainer": [
|
||||
r"\.BorderStyle = System\.Windows\.Forms\.BorderStyle\.None;$",
|
||||
r"\.FixedPanel = System\.Windows\.Forms\.FixedPanel\.None;$",
|
||||
r"\.IsSplitterFixed = false;$",
|
||||
r"\.Orientation = System\.Windows\.Forms\.Orientation\.Vertical;$",
|
||||
r"\.Panel1Collapsed = false;$",
|
||||
r"\.Panel2Collapsed = false;$",
|
||||
r"\.Panel1MinSize = 25;$",
|
||||
r"\.Panel2MinSize = 25;$",
|
||||
r"\.SplitterIncrement = 1;$",
|
||||
r"\.SplitterWidth = 4;$",
|
||||
r"\.BackColor = System\.Drawing\.SystemColors\.Control;$",
|
||||
],
|
||||
"TabControl": [
|
||||
r"\.Alignment = System\.Windows\.Forms\.TabAlignment\.Top;$",
|
||||
r"\.Appearance = System\.Windows\.Forms\.TabAppearance\.Normal;$",
|
||||
r"\.DrawMode = System\.Windows\.Forms\.TabDrawMode\.Normal;$",
|
||||
r"\.HotTrack = false;$",
|
||||
r"\.Multiline = false;$",
|
||||
r"\.RightToLeftLayout = false;$",
|
||||
r"\.ShowToolTips = false;$",
|
||||
r"\.BackColor = System\.Drawing\.SystemColors\.Control;$",
|
||||
],
|
||||
"TabPage": [
|
||||
r"\.BorderStyle = System\.Windows\.Forms\.BorderStyle\.None;$",
|
||||
r"\.AutoScrollMargin = new System\.Drawing\.Size\(0, 0\);$",
|
||||
r"\.AutoScrollMinSize = new System\.Drawing\.Size\(0, 0\);$",
|
||||
],
|
||||
"Button": [
|
||||
r"\.BackColor = System\.Drawing\.SystemColors\.Control;$",
|
||||
r"\.TextAlign = System\.Drawing\.ContentAlignment\.MiddleCenter;$",
|
||||
],
|
||||
"Label": [
|
||||
r"\.BackColor = System\.Drawing\.SystemColors\.Control;$",
|
||||
],
|
||||
}
|
||||
|
||||
DROP_GLOBAL_C = [re.compile(p) for p in DROP_GLOBAL]
|
||||
DROP_BY_TYPE_C = {k: [re.compile(p) for p in v] for k, v in DROP_BY_TYPE.items()}
|
||||
|
||||
|
||||
def short_type(t):
|
||||
return t.split(".")[-1]
|
||||
|
||||
|
||||
def should_drop(stmt, stype):
|
||||
for c in DROP_GLOBAL_C:
|
||||
if c.search(stmt):
|
||||
return True
|
||||
for c in DROP_BY_TYPE_C.get(stype, []):
|
||||
if c.search(stmt):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 列转换
|
||||
|
||||
def extract_columns(stmts):
|
||||
cols = []
|
||||
rest = []
|
||||
for st in stmts:
|
||||
m = re.match(r"this\.(" + NAME + r")\.Columns\.Add\(new System\.Windows\.Forms\.(\w+)\(\)\);$", st, re.S)
|
||||
if m:
|
||||
cols.append({"type": m.group(2), "props": []})
|
||||
continue
|
||||
m = re.match(r"this\.(" + NAME + r")\.Columns\[(\d+)\]\.([\w.]+) = (.*);$", st, re.S)
|
||||
if m:
|
||||
idx = int(m.group(2))
|
||||
while len(cols) <= idx:
|
||||
cols.append({"type": "DataGridViewTextBoxColumn", "props": []})
|
||||
cols[idx]["props"].append((m.group(3), m.group(4)))
|
||||
continue
|
||||
rest.append(st)
|
||||
return rest, cols
|
||||
|
||||
|
||||
COL_DROP_VALUE = {
|
||||
"Visible": "true",
|
||||
"Frozen": "false",
|
||||
"AutoSizeMode": "System.Windows.Forms.DataGridViewAutoSizeColumnMode.NotSet",
|
||||
"Width": "100",
|
||||
"Resizable": "System.Windows.Forms.DataGridViewTriState.True",
|
||||
}
|
||||
|
||||
|
||||
def column_lines(col):
|
||||
out = []
|
||||
for prop, val in col["props"]:
|
||||
if prop in COL_DROP_VALUE and val == COL_DROP_VALUE[prop]:
|
||||
continue
|
||||
if prop == "Name" and val == '""':
|
||||
continue
|
||||
out.append((prop, val))
|
||||
return out
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 子树工具
|
||||
|
||||
def build_children(edges):
|
||||
ch = {}
|
||||
for parent, kids in edges:
|
||||
ch.setdefault(parent, []).extend(kids)
|
||||
return ch
|
||||
|
||||
|
||||
def subtree(root, ch):
|
||||
out = []
|
||||
stack = [root]
|
||||
while stack:
|
||||
n = stack.pop(0)
|
||||
out.append(n)
|
||||
stack = ch.get(n, []) + ch.get(n + ".Panel1", []) + ch.get(n + ".Panel2", []) + stack
|
||||
return out
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 通用块生成
|
||||
|
||||
def plan_columns(ordered, ctrl_blocks):
|
||||
"""预规划列字段:返回 {grid: [(fname, col)]},字段按 ordered 顺序编号"""
|
||||
plan = {}
|
||||
n = 0
|
||||
for g in ordered:
|
||||
_rest, cols = extract_columns(ctrl_blocks.get(g, []))
|
||||
if cols:
|
||||
flist = []
|
||||
for col in cols:
|
||||
n += 1
|
||||
flist.append(("dataGridViewTextBoxColumn%d" % n, col))
|
||||
plan[g] = flist
|
||||
return plan
|
||||
|
||||
|
||||
def emit_control_blocks(ordered, decl_types, ctrl_blocks, ts_edges, col_plan, a):
|
||||
"""生成属性块"""
|
||||
for n in ordered:
|
||||
stype = short_type(decl_types[n])
|
||||
stmts = ctrl_blocks.get(n, [])
|
||||
rest, cols = extract_columns(stmts)
|
||||
# 工具栏项
|
||||
ts_items = None
|
||||
for parent, kk in ts_edges:
|
||||
if parent == n:
|
||||
ts_items = kk
|
||||
if not rest and not cols and ts_items is None:
|
||||
continue
|
||||
if n in col_plan:
|
||||
for fname, col in col_plan[n]:
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\t// %s" % fname)
|
||||
a("\t\t\t// ")
|
||||
for prop, val in column_lines(col):
|
||||
a("\t\t\tthis.%s.%s = %s;" % (fname, prop, val))
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\t// %s" % n)
|
||||
a("\t\t\t// ")
|
||||
for st in rest:
|
||||
if should_drop(st, stype):
|
||||
continue
|
||||
a("\t\t\t" + st.replace("\n", "\n\t\t\t"))
|
||||
if cols:
|
||||
inner = ",\n\t\t\t\t".join("this.%s" % fname for fname, _c in col_plan[n])
|
||||
a("\t\t\tthis.%s.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {" % n)
|
||||
a("\t\t\t\t" + inner + "});")
|
||||
if ts_items is not None:
|
||||
inner = ",\n\t\t\t\t".join("this.%s" % k for k in ts_items)
|
||||
a("\t\t\tthis.%s.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {" % n)
|
||||
a("\t\t\t\t" + inner + "});")
|
||||
|
||||
|
||||
TEMPLATE = """namespace 成套报价软件
|
||||
{{
|
||||
partial class {cls}
|
||||
{{
|
||||
/// <summary>
|
||||
/// Designer variable used to keep track of non-visual components.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
{fields}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes resources used by the form.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{{
|
||||
if (disposing && (components != null)) {{
|
||||
components.Dispose();
|
||||
}}
|
||||
base.Dispose(disposing);
|
||||
}}
|
||||
|
||||
#region Designer generated code
|
||||
|
||||
/// <summary>
|
||||
/// This method is required for Windows Forms designer support.
|
||||
/// Do not change the method contents inside the source code editor. The Forms designer might
|
||||
/// not be able to load this method if it was changed manually.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{{
|
||||
{body}
|
||||
}}
|
||||
|
||||
#endregion
|
||||
}}
|
||||
}}
|
||||
"""
|
||||
|
||||
|
||||
def emit_decls(ordered, decl_types, begin_inits, a):
|
||||
for n in ordered:
|
||||
a("\t\t\tthis.%s = new %s();" % (n, decl_types[n]))
|
||||
if n in begin_inits:
|
||||
a("\t\t\t((System.ComponentModel.ISupportInitialize)(this.%s)).BeginInit();" % n)
|
||||
|
||||
|
||||
def emit_hierarchy(ordered_set, edges, ch, a, root=None):
|
||||
"""输出容器层级;root 的孩子挂 this"""
|
||||
for parent, kids in edges:
|
||||
if parent == "":
|
||||
continue
|
||||
if parent == root:
|
||||
target = "this"
|
||||
else:
|
||||
base = parent.split(".")[0]
|
||||
if base not in ordered_set:
|
||||
continue
|
||||
target = "this." + parent
|
||||
if not kids:
|
||||
continue
|
||||
inner = ",\n\t\t\t\t".join("this.%s" % k for k in kids)
|
||||
a("\t\t\t%s.Controls.AddRange(new System.Windows.Forms.Control[] {" % target)
|
||||
a("\t\t\t\t" + inner + "});")
|
||||
|
||||
|
||||
def gen_dockwindow(cls, tab, title, decls, decl_types, begin_inits, ctrl_blocks, ch, edges, ts_edges, splitter_map):
|
||||
members = subtree(tab, ch)
|
||||
members.remove(tab)
|
||||
members_set = set(members)
|
||||
ordered = [n for n, _t in decls if n in members_set]
|
||||
# 工具栏项不在 Controls 层级里,补进来
|
||||
for parent, kids in ts_edges:
|
||||
if parent in members_set:
|
||||
for k in kids:
|
||||
if k not in ordered:
|
||||
ordered.append(k)
|
||||
|
||||
tab_size = (800, 600)
|
||||
for st in ctrl_blocks.get(tab, []):
|
||||
m = re.match(r"this\.(" + NAME + r")\.Size = new System\.Drawing\.Size\((\d+), (\d+)\);$", st)
|
||||
if m:
|
||||
tab_size = (int(m.group(2)), int(m.group(3)))
|
||||
|
||||
L = []
|
||||
a = L.append
|
||||
a("\t\t\tthis.components = new System.ComponentModel.Container();")
|
||||
emit_decls(ordered, decl_types, begin_inits, a)
|
||||
col_plan = plan_columns(ordered, ctrl_blocks)
|
||||
for flist in col_plan.values():
|
||||
for fname, _c in flist:
|
||||
a("\t\t\tthis.%s = new System.Windows.Forms.DataGridViewTextBoxColumn();" % fname)
|
||||
a("\t\t\tthis.SuspendLayout();")
|
||||
emit_control_blocks(ordered, decl_types, ctrl_blocks, ts_edges, col_plan, a)
|
||||
# 层级
|
||||
emit_hierarchy(members_set, edges, ch, a, root=tab)
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\t// %s" % cls)
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\tthis.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);")
|
||||
a("\t\t\tthis.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;")
|
||||
a("\t\t\tthis.ClientSize = new System.Drawing.Size(%d, %d);" % tab_size)
|
||||
a("\t\t\tthis.Name = \"%s\";" % cls)
|
||||
a("\t\t\tthis.Text = \"%s\";" % title)
|
||||
for n in ordered:
|
||||
if n in begin_inits:
|
||||
a("\t\t\t((System.ComponentModel.ISupportInitialize)(this.%s)).EndInit();" % n)
|
||||
a("\t\t\tthis.ResumeLayout(false);")
|
||||
a("\t\t\tthis.PerformLayout();")
|
||||
for n in ordered:
|
||||
if n in splitter_map:
|
||||
a("\t\t\tthis.%s.SplitterDistance = %d;" % (n, splitter_map[n]))
|
||||
|
||||
fields = ["\t\tprivate %s %s;" % (decl_types[n], n) for n in ordered]
|
||||
for flist in col_plan.values():
|
||||
for fname, _c in flist:
|
||||
fields.append("\t\tprivate System.Windows.Forms.DataGridViewTextBoxColumn %s;" % fname)
|
||||
return TEMPLATE.format(cls=cls, body="\n".join(L), fields="\n".join(fields))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- MainForm
|
||||
|
||||
def gen_mainform(decls, decl_types, begin_inits, ctrl_blocks, ch, edges, ts_edges, splitter_map):
|
||||
# MainForm 使用的旧控件:tabControl1 三页签的子树 + statusStrip1
|
||||
main_roots = ["tabPage1", "tabPage2", "tabPage8"]
|
||||
members = set()
|
||||
for r in main_roots:
|
||||
members.update(subtree(r, ch))
|
||||
members.remove("tabPage1"); members.remove("tabPage2"); members.remove("tabPage8")
|
||||
members.add("statusStrip1")
|
||||
ordered = [n for n, _t in decls if n in members]
|
||||
for parent, kids in ts_edges:
|
||||
if parent in members:
|
||||
for k in kids:
|
||||
if k not in ordered:
|
||||
ordered.append(k)
|
||||
|
||||
L = []
|
||||
a = L.append
|
||||
for i in range(1, 10):
|
||||
a("\t\t\tSmart.CustomComponents.DockWindows.DockWindowItem dockWindowItem%d = new Smart.CustomComponents.DockWindows.DockWindowItem();" % i)
|
||||
a("\t\t\tthis.tabControl1 = new System.Windows.Forms.TabControl();")
|
||||
a("\t\t\tthis.tabPage1 = new System.Windows.Forms.TabPage();")
|
||||
a("\t\t\tthis.tabPage2 = new System.Windows.Forms.TabPage();")
|
||||
a("\t\t\tthis.tabPage3 = new System.Windows.Forms.TabPage();")
|
||||
for n in ordered:
|
||||
a("\t\t\tthis.%s = new %s();" % (n, decl_types[n]))
|
||||
a("\t\t\tthis.dockSurface1 = new Smart.CustomComponents.DockSurface();")
|
||||
col_plan = plan_columns(ordered, ctrl_blocks)
|
||||
for flist in col_plan.values():
|
||||
for fname, _c in flist:
|
||||
a("\t\t\tthis.%s = new System.Windows.Forms.DataGridViewTextBoxColumn();" % fname)
|
||||
a("\t\t\tthis.tabControl1.SuspendLayout();")
|
||||
a("\t\t\tthis.SuspendLayout();")
|
||||
|
||||
emit_control_blocks(ordered, decl_types, ctrl_blocks, ts_edges, col_plan, a)
|
||||
|
||||
# tabControl1 属性(旧值 + 新尺寸)
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\t// tabControl1")
|
||||
a("\t\t\t// ")
|
||||
tc_props = []
|
||||
for st in ctrl_blocks.get("tabControl1", []):
|
||||
if should_drop(st, "TabControl"):
|
||||
continue
|
||||
if re.search(r"\.(Size|Location|Name|TabIndex|Dock) = ", st):
|
||||
continue
|
||||
tc_props.append(st)
|
||||
for st in tc_props:
|
||||
a("\t\t\t" + st)
|
||||
a("\t\t\tthis.tabControl1.Dock = System.Windows.Forms.DockStyle.Top;")
|
||||
a("\t\t\tthis.tabControl1.Location = new System.Drawing.Point(0, 0);")
|
||||
a("\t\t\tthis.tabControl1.Name = \"tabControl1\";")
|
||||
a("\t\t\tthis.tabControl1.SelectedIndex = 0;")
|
||||
a("\t\t\tthis.tabControl1.Size = new System.Drawing.Size(1884, 111);")
|
||||
a("\t\t\tthis.tabControl1.TabIndex = 0;")
|
||||
|
||||
# 三个页签
|
||||
for old, new, text in [("tabPage1", "tabPage1", "项目"), ("tabPage2", "tabPage2", "报价"), ("tabPage8", "tabPage3", "设置")]:
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\t// %s" % new)
|
||||
a("\t\t\t// ")
|
||||
kids = ch.get(old, [])
|
||||
if kids:
|
||||
inner = ",\n\t\t\t\t".join("this.%s" % k for k in kids)
|
||||
a("\t\t\tthis.%s.Controls.AddRange(new System.Windows.Forms.Control[] {" % new)
|
||||
a("\t\t\t\t" + inner + "});")
|
||||
for st in ctrl_blocks.get(old, []):
|
||||
if should_drop(st, "TabPage"):
|
||||
continue
|
||||
if re.search(r"\.(Size|Location|Name|TabIndex|Padding|Text|BackColor) = ", st):
|
||||
continue
|
||||
a("\t\t\t" + re.sub(r"^this\.%s\b" % old, "this.%s" % new, st))
|
||||
a("\t\t\tthis.%s.Location = new System.Drawing.Point(4, 22);" % new)
|
||||
a("\t\t\tthis.%s.Name = \"%s\";" % (new, new))
|
||||
a("\t\t\tthis.%s.Padding = new System.Windows.Forms.Padding(3, 3, 3, 3);" % new)
|
||||
a("\t\t\tthis.%s.Size = new System.Drawing.Size(1876, 82);" % new)
|
||||
a("\t\t\tthis.%s.TabIndex = %d;" % (new, ["tabPage1", "tabPage2", "tabPage3"].index(new)))
|
||||
a("\t\t\tthis.%s.Text = \"%s\";" % (new, text))
|
||||
a("\t\t\tthis.%s.UseVisualStyleBackColor = true;" % new)
|
||||
|
||||
# statusStrip1
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\t// statusStrip1")
|
||||
a("\t\t\t// ")
|
||||
for st in ctrl_blocks.get("statusStrip1", []):
|
||||
if should_drop(st, "StatusStrip"):
|
||||
continue
|
||||
if re.search(r"\.(Location|Size|Name|TabIndex) = ", st):
|
||||
continue
|
||||
a("\t\t\t" + st)
|
||||
a("\t\t\tthis.statusStrip1.Dock = System.Windows.Forms.DockStyle.Bottom;")
|
||||
a("\t\t\tthis.statusStrip1.Location = new System.Drawing.Point(0, 932);")
|
||||
a("\t\t\tthis.statusStrip1.Name = \"statusStrip1\";")
|
||||
a("\t\t\tthis.statusStrip1.Size = new System.Drawing.Size(1884, 22);")
|
||||
a("\t\t\tthis.statusStrip1.TabIndex = 2;")
|
||||
|
||||
# dockSurface1(保持现有内容)
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\t// dockSurface1")
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\tthis.dockSurface1.Dock = System.Windows.Forms.DockStyle.Fill;")
|
||||
a("\t\t\tthis.dockSurface1.DockBackColor = System.Drawing.Color.FromArgb(((int)(((byte)(238)))), ((int)(((byte)(238)))), ((int)(((byte)(242)))));")
|
||||
a("\t\t\tdockWindowItem1.DockState = WeifenLuo.WinFormsUI.Docking.DockState.Document;")
|
||||
a("\t\t\tdockWindowItem1.Title = \"项目\";")
|
||||
a("\t\t\tdockWindowItem2.ClassName = \"DockWindow2\";")
|
||||
a("\t\t\tdockWindowItem2.DockState = WeifenLuo.WinFormsUI.Docking.DockState.Document;")
|
||||
a("\t\t\tdockWindowItem2.Title = \"报价\";")
|
||||
a("\t\t\tdockWindowItem3.ClassName = \"DockWindow3\";")
|
||||
a("\t\t\tdockWindowItem3.DockState = WeifenLuo.WinFormsUI.Docking.DockState.Document;")
|
||||
a("\t\t\tdockWindowItem3.Title = \"选型\";")
|
||||
a("\t\t\tdockWindowItem4.ClassName = \"DockWindow4\";")
|
||||
a("\t\t\tdockWindowItem4.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockRight;")
|
||||
a("\t\t\tdockWindowItem4.Title = \"元件库\";")
|
||||
a("\t\t\tdockWindowItem5.ClassName = \"DockWindow5\";")
|
||||
a("\t\t\tdockWindowItem5.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockRight;")
|
||||
a("\t\t\tdockWindowItem5.Title = \"用户库\";")
|
||||
a("\t\t\tdockWindowItem6.ClassName = \"DockWindow6\";")
|
||||
a("\t\t\tdockWindowItem6.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockRight;")
|
||||
a("\t\t\tdockWindowItem6.Title = \"柜体库\";")
|
||||
a("\t\t\tdockWindowItem7.ClassName = \"DockWindow7\";")
|
||||
a("\t\t\tdockWindowItem7.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockRight;")
|
||||
a("\t\t\tdockWindowItem7.Title = \"母排\";")
|
||||
a("\t\t\tdockWindowItem8.ClassName = \"DockWindow8\";")
|
||||
a("\t\t\tdockWindowItem8.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockRight;")
|
||||
a("\t\t\tdockWindowItem8.Title = \"方案\";")
|
||||
a("\t\t\tdockWindowItem9.ClassName = \"DockWindow9\";")
|
||||
a("\t\t\tdockWindowItem9.DockState = WeifenLuo.WinFormsUI.Docking.DockState.DockRight;")
|
||||
a("\t\t\tdockWindowItem9.Title = \"箱体计算\";")
|
||||
for i in range(1, 10):
|
||||
a("\t\t\tthis.dockSurface1.DockWindows.Add(dockWindowItem%d);" % i)
|
||||
a("\t\t\tthis.dockSurface1.Location = new System.Drawing.Point(0, 111);")
|
||||
a("\t\t\tthis.dockSurface1.Name = \"dockSurface1\";")
|
||||
a("\t\t\tthis.dockSurface1.Padding = new System.Windows.Forms.Padding(6);")
|
||||
a("\t\t\tthis.dockSurface1.ShowAutoHideContentOnHover = false;")
|
||||
a("\t\t\tthis.dockSurface1.Size = new System.Drawing.Size(1884, 821);")
|
||||
a("\t\t\tthis.dockSurface1.TabIndex = 1;")
|
||||
|
||||
# 窗体
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\t// MainForm")
|
||||
a("\t\t\t// ")
|
||||
a("\t\t\tthis.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);")
|
||||
a("\t\t\tthis.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;")
|
||||
a("\t\t\tthis.ClientSize = new System.Drawing.Size(1884, 954);")
|
||||
a("\t\t\tthis.Controls.Add(this.dockSurface1);")
|
||||
a("\t\t\tthis.Controls.Add(this.statusStrip1);")
|
||||
a("\t\t\tthis.Controls.Add(this.tabControl1);")
|
||||
a("\t\t\tthis.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2);")
|
||||
a("\t\t\tthis.Name = \"MainForm\";")
|
||||
a("\t\t\tthis.Text = \"成套报价软件\";")
|
||||
a("\t\t\tthis.tabControl1.ResumeLayout(false);")
|
||||
a("\t\t\tthis.ResumeLayout(false);")
|
||||
a("\t\t\tthis.PerformLayout();")
|
||||
|
||||
fields = []
|
||||
fields.append("\t\tprivate System.Windows.Forms.TabControl tabControl1;")
|
||||
fields.append("\t\tprivate System.Windows.Forms.TabPage tabPage1;")
|
||||
fields.append("\t\tprivate System.Windows.Forms.TabPage tabPage2;")
|
||||
fields.append("\t\tprivate System.Windows.Forms.TabPage tabPage3;")
|
||||
for n in ordered:
|
||||
fields.append("\t\tprivate %s %s;" % (decl_types[n], n))
|
||||
fields.append("\t\tprivate Smart.CustomComponents.DockSurface dockSurface1;")
|
||||
for flist in col_plan.values():
|
||||
for fname, _c in flist:
|
||||
fields.append("\t\tprivate System.Windows.Forms.DataGridViewTextBoxColumn %s;" % fname)
|
||||
|
||||
header = """/*
|
||||
* 由SharpDevelop创建。
|
||||
* 用户: 099978
|
||||
* 日期: 2026-08-25
|
||||
* 时间: 20:59
|
||||
*
|
||||
* 要改变这种模板请点击 工具|选项|代码编写|编辑标准头文件
|
||||
*/
|
||||
namespace 成套报价软件
|
||||
{
|
||||
partial class MainForm
|
||||
{
|
||||
/// <summary>
|
||||
/// Designer variable used to keep track of non-visual components.
|
||||
/// </summary>
|
||||
private System.ComponentModel.IContainer components = null;
|
||||
|
||||
%s
|
||||
|
||||
/// <summary>
|
||||
/// Disposes resources used by the form.
|
||||
/// </summary>
|
||||
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing) {
|
||||
if (components != null) {
|
||||
components.Dispose();
|
||||
}
|
||||
}
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This method is required for Windows Forms designer support.
|
||||
/// Do not change the method contents inside the source code editor. The Forms designer might
|
||||
/// not be able to load this method if it was changed manually.
|
||||
/// </summary>
|
||||
private void InitializeComponent()
|
||||
{
|
||||
%s
|
||||
}
|
||||
}
|
||||
}
|
||||
""" % ("\n".join(fields), "\n".join(L))
|
||||
return header
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- main
|
||||
|
||||
def main():
|
||||
text = io.open(SRC, encoding="utf-8-sig").read()
|
||||
decls, begin_inits, blocks, top_statements, edges, ts_edges = parse(text)
|
||||
ctrl_blocks, form_block = classify(blocks)
|
||||
ch = build_children(edges)
|
||||
decl_types = dict(decls)
|
||||
|
||||
splitter_map = {}
|
||||
for st in form_block + top_statements:
|
||||
m = re.match(r"this\.(" + NAME + r")\.SplitterDistance = (\d+);$", st)
|
||||
if m:
|
||||
splitter_map[m.group(1)] = int(m.group(2))
|
||||
|
||||
missing = [t for _c, t, _ti in DOCK_MAP if t not in decl_types]
|
||||
if missing:
|
||||
print("缺少页签:", missing)
|
||||
sys.exit(1)
|
||||
|
||||
for cls, tab, title in DOCK_MAP:
|
||||
out = gen_dockwindow(cls, tab, title, decls, decl_types, begin_inits, ctrl_blocks, ch, edges, ts_edges, splitter_map)
|
||||
path = os.path.join(DST_PROJECT, "DockWindows", cls, cls + ".Designer.cs")
|
||||
io.open(path, "w", encoding="utf-8-sig", newline="\r\n").write(out)
|
||||
n_ctrl = len(subtree(tab, ch)) - 1
|
||||
print("生成 %-60s ← %-9s (%s) 控件%d个" % (path, tab, title, n_ctrl))
|
||||
|
||||
mf = gen_mainform(decls, decl_types, begin_inits, ctrl_blocks, ch, edges, ts_edges, splitter_map)
|
||||
path = os.path.join(DST_PROJECT, "MainForm.Designer.cs")
|
||||
io.open(path, "w", encoding="utf-8-sig", newline="\r\n").write(mf)
|
||||
print("生成", path)
|
||||
|
||||
# 覆盖检查
|
||||
covered = set()
|
||||
for _c, tab, _t in DOCK_MAP:
|
||||
covered.update(subtree(tab, ch))
|
||||
main_used = set(["tabControl1", "tabControl2", "tabControl3", "splitContainer1", "statusStrip1"])
|
||||
for r in ["tabControl1", "statusStrip1"]:
|
||||
main_used.update(subtree(r, ch))
|
||||
leftover = [n for n, _ in decls if n not in covered and n not in main_used]
|
||||
print("未覆盖控件:", leftover)
|
||||
|
||||
# 边完整性检查
|
||||
all_kids = [k for _p, kk in edges for k in kk]
|
||||
no_parent = [k for k in all_kids if k not in decl_types]
|
||||
print("边中未声明的子控件:", no_parent)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,115 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""触发器创建挪到数据写完后统一建。幂等(基于标记检查)。"""
|
||||
import io
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\主窗口.菜单与库.cs"
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件"
|
||||
n = 0
|
||||
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
|
||||
def rep(old, new, tag):
|
||||
global s, n
|
||||
if old not in s:
|
||||
if new in s:
|
||||
print(tag, "已有(跳过)")
|
||||
return
|
||||
raise AssertionError(tag + " 锚点缺失: " + old[:70])
|
||||
s = s.replace(old, new, 1)
|
||||
n += 1
|
||||
print(tag, "ok")
|
||||
|
||||
# 1) 字段(编辑直写库.cs, tab 缩进)
|
||||
p2 = ROOT + r"\编辑直写库.cs"
|
||||
s2 = io.open(p2, encoding="utf-8-sig").read()
|
||||
old_f = "\t\t/// <summary>当前 Reload 耗时测量(进 Reload 方法时重启)。</summary>"
|
||||
new_f = ("\t\t/// <summary>待建触发器清单 [yj表, 箱号表, 箱号ID, 图号ID](数据写完后统一建,避免事务内 DDL 拖慢批量 INSERT)。</summary>\n"
|
||||
"\t\tprivate readonly System.Collections.Generic.List<string[]> _待建yj触发器 = new System.Collections.Generic.List<string[]>();\n"
|
||||
"\t\t/// <summary>待建箱号表触发器 [箱号表, 图号ID]。</summary>\n"
|
||||
"\t\tprivate readonly System.Collections.Generic.List<string[]> _待建xh触发器 = new System.Collections.Generic.List<string[]>();\n\n"
|
||||
"\t\t/// <summary>当前 Reload 耗时测量(进 Reload 方法时重启)。</summary>")
|
||||
if old_f in s2 and "_待建yj触发器" not in s2:
|
||||
s2 = s2.replace(old_f, new_f, 1)
|
||||
io.open(p2, "w", encoding="utf-8-sig").write(s2)
|
||||
n += 1
|
||||
print("字段 ok")
|
||||
else:
|
||||
print("字段 已有(跳过)")
|
||||
|
||||
# 2) 图号管线循环内:建元件表触发器 → 收集
|
||||
rep(''' // ★ 建元件表触发器:INSERT/UPDATE/DELETE 自动算派生价+刷新箱号/图号聚合
|
||||
计算引擎.建元件表触发器(conn, tx, yjTbl, xhTbl, nx, nt, TuhaoTableName,
|
||||
_公式_元件, _公式_箱号, _公式_图号);''',
|
||||
''' // ★ 登记待建触发器(数据全部写完后统一建,避免事务内 DDL 拖慢 INSERT)
|
||||
_待建yj触发器.Add(new[] { yjTbl, xhTbl, nx, nt });''', "图号管线yj收集")
|
||||
|
||||
# 3) 图号管线:建箱号表触发器 → 收集
|
||||
rep(''' // ★ 建箱号表触发器:INSERT/UPDATE/DELETE 自动算行内公式+刷新图号聚合
|
||||
计算引擎.建箱号表触发器(conn, tx, xhTbl, nt, TuhaoTableName, new List<string[]>(), _公式_箱号, _公式_图号);''',
|
||||
''' // 箱号表触发器管尾统一建(行级依赖各行的元件表名)
|
||||
if (!_待建xh触发器.Exists(x => x[0] == xhTbl)) _待建xh触发器.Add(new[] { xhTbl, nt });''', "图号管线xh收集")
|
||||
|
||||
# 4) 图号管线管尾:统一建(开关=0 前)
|
||||
rep(''' // ★ 恢复触发器(编辑自动计算)
|
||||
计算引擎.开关设(conn, tx, 0);''',
|
||||
''' // ★ 数据全部写完,统一建触发器(此后无批量 DML)
|
||||
foreach (var t in _待建yj触发器)
|
||||
计算引擎.建元件表触发器(conn, tx, t[0], t[1], t[2], t[3], TuhaoTableName,
|
||||
_公式_元件, _公式_箱号, _公式_图号);
|
||||
foreach (var t in _待建xh触发器)
|
||||
{
|
||||
var rows = new System.Collections.Generic.List<string[]>();
|
||||
foreach (var y in _待建yj触发器)
|
||||
if (y[1] == t[0]) rows.Add(new[] { y[2], y[0] });
|
||||
计算引擎.建箱号表触发器(conn, tx, t[0], t[1], TuhaoTableName, rows, _公式_箱号, _公式_图号);
|
||||
}
|
||||
_待建yj触发器.Clear();
|
||||
_待建xh触发器.Clear();
|
||||
// ★ 恢复触发器(编辑自动计算)
|
||||
计算引擎.开关设(conn, tx, 0);''', "图号管线管尾")
|
||||
|
||||
# 5) PasteYuanjianRowsToDb 建新表:触发器 → 收集
|
||||
rep(''' // ★ 新元件表:建公式触发器(INSERT/UPDATE/DELETE 自动算派生价+刷聚合)
|
||||
string _xhTblReal = LookupTableByIndex("箱号", tuhao, "", conn);
|
||||
if (!string.IsNullOrEmpty(_xhTblReal))
|
||||
{
|
||||
using (var fakConn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(
|
||||
System.IO.Path.Combine(System.IO.Path.GetDirectoryName(GetQuoteDbPath(projectId)), "fak.db"))))
|
||||
{
|
||||
fakConn.Open();
|
||||
计算引擎.EnsureFormulaTable(fakConn);
|
||||
计算引擎.建元件表触发器(conn, tx, tblName, _xhTblReal, xhId, tuhao, TuhaoTableName,
|
||||
计算引擎.加载公式(fakConn, "元件"), 计算引擎.加载公式(fakConn, "箱号"),
|
||||
计算引擎.加载公式(fakConn, "图号"));
|
||||
}
|
||||
}''',
|
||||
''' // ★ 登记待建触发器(管尾统一建;行内派生值已随行写入,无需即时触发)
|
||||
string _xhTblReal = LookupTableByIndex("箱号", tuhao, "", conn);
|
||||
if (!string.IsNullOrEmpty(_xhTblReal))
|
||||
{
|
||||
bool exists = false;
|
||||
foreach (var t in _待建yj触发器) if (t[2] == xhId && t[3] == tuhao) { exists = true; break; }
|
||||
if (!exists) _待建yj触发器.Add(new[] { tblName, _xhTblReal, xhId, tuhao });
|
||||
if (!_待建xh触发器.Exists(x => x[0] == _xhTblReal && x[1] == tuhao)) _待建xh触发器.Add(new[] { _xhTblReal, tuhao });
|
||||
}''', "PasteYuanjian收集")
|
||||
|
||||
# 6) 箱号管线管尾:统一建(开关=0 前) —— 锚点用 刷新图号行聚合 调用行
|
||||
rep(''' // 箱号行聚合抄源行已正确;仅图号行聚合(全箱号表 SUM)需刷新一次
|
||||
计算引擎.刷新图号行聚合(conn, tx, dbPath, th, TuhaoTableName);''',
|
||||
''' // 箱号行聚合抄源行已正确;统一建触发器 + 刷一次图号行聚合
|
||||
foreach (var t in _待建yj触发器)
|
||||
计算引擎.建元件表触发器(conn, tx, t[0], t[1], t[2], t[3], TuhaoTableName,
|
||||
_公式_元件, _公式_箱号, _公式_图号);
|
||||
foreach (var t in _待建xh触发器)
|
||||
{
|
||||
var rows = new System.Collections.Generic.List<string[]>();
|
||||
foreach (var y in _待建yj触发器)
|
||||
if (y[1] == t[0] && y[3] == t[1]) rows.Add(new[] { y[2], y[0] });
|
||||
计算引擎.建箱号表触发器(conn, tx, t[0], t[1], TuhaoTableName, rows, _公式_箱号, _公式_图号);
|
||||
}
|
||||
_待建yj触发器.Clear();
|
||||
_待建xh触发器.Clear();
|
||||
计算引擎.刷新图号行聚合(conn, tx, dbPath, th, TuhaoTableName);''', "箱号管线管尾")
|
||||
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("合计", n, "处")
|
||||
@@ -0,0 +1,253 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""按用户洞察修正:粘贴/删除时数据本身正确,关掉触发器直接写;只有编辑(改输入)才触发计算。
|
||||
①计算引擎:开关表+触发器 WHEN 开关=0+刷新图号行聚合方法
|
||||
②图号粘贴:箱号行/图号行 INSERT 直接抄源行的聚合值(不再写 0);管线开关=1 包裹(零重算)
|
||||
③箱号粘贴:开关=1 包裹,管尾刷一次图号行聚合
|
||||
④删除管线:开关=1 包裹,管尾刷一次图号行聚合"""
|
||||
import io
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
|
||||
n = 0
|
||||
|
||||
def edit(path, pairs, tag):
|
||||
global n
|
||||
s = io.open(path, encoding="utf-8-sig").read()
|
||||
for old, new in pairs:
|
||||
if old not in s:
|
||||
if new in s:
|
||||
print(tag, "已有(跳过):", old[:40].replace("\n", " "))
|
||||
continue
|
||||
raise AssertionError(tag + " 锚点缺失: " + old[:80])
|
||||
s = s.replace(old, new, 1)
|
||||
n += 1
|
||||
io.open(path, "w", encoding="utf-8-sig").write(s)
|
||||
print(tag, "ok", n, "累计")
|
||||
|
||||
# ============ ① 计算引擎 ============
|
||||
p = ROOT + r"\计算引擎.cs"
|
||||
edit(p, [
|
||||
# 开关条件常量
|
||||
(''' /// <summary>触发器名前缀(重建时按前缀全清)。</summary>
|
||||
public const string 触发器前缀 = "trgjs_";''',
|
||||
''' /// <summary>触发器名前缀(重建时按前缀全清)。</summary>
|
||||
public const string 触发器前缀 = "trgjs_";
|
||||
|
||||
/// <summary>触发器总开关条件:粘贴/删除批量写时置 开关=1(数据自带正确值,无需触发器计算)。</summary>
|
||||
public const string 开关条件 = "(SELECT 开关 FROM _触发器开关 WHERE id=1)=0";'''),
|
||||
# 触发器加 WHEN 开关=0(元件 u/i/d)
|
||||
(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_u AFTER UPDATE OF " + of + " ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet +''',
|
||||
''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_u AFTER UPDATE OF " + of + " ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet +'''),
|
||||
(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_i AFTER INSERT ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet +''',
|
||||
''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_i AFTER INSERT ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet +'''),
|
||||
(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_d AFTER DELETE ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW BEGIN " + 刷箱号 + 刷图号 + "END;");''',
|
||||
''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_d AFTER DELETE ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN " + 刷箱号 + 刷图号 + "END;");'''),
|
||||
# 箱号行级 + i/d
|
||||
(''' "_u AFTER UPDATE OF " + of + " ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" +
|
||||
箱号ID.Replace("'", "''") + "' BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew +''',
|
||||
''' "_u AFTER UPDATE OF " + of + " ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" +
|
||||
箱号ID.Replace("'", "''") + "' AND " + 开关条件 + " BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew +'''),
|
||||
(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");''',
|
||||
''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN " + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN " + 刷图号 + "END;");'''),
|
||||
# 确保开关表(重建时)+开关设+刷新图号行聚合
|
||||
(''' using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"CREATE TABLE IF NOT EXISTS _触发器开关 (id INTEGER PRIMARY KEY, 开关 INTEGER DEFAULT 0);", conn, tx))
|
||||
cmd.ExecuteNonQuery();
|
||||
Exec(conn, tx, "INSERT OR IGNORE INTO _触发器开关 (id, 开关) VALUES (1, 0);");
|
||||
|
||||
var old = new List<string>();''',
|
||||
''' var old = new List<string>();'''),
|
||||
(''' /// <summary>新表(管线内建表后)即时补触发器——只处理该表自身。</summary>''',
|
||||
''' /// <summary>确保开关表存在(项目库;老库首次自动建)。</summary>
|
||||
public static void 确保开关表(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx)
|
||||
{
|
||||
Exec(conn, tx, "CREATE TABLE IF NOT EXISTS _触发器开关 (id INTEGER PRIMARY KEY, 开关 INTEGER DEFAULT 0);");
|
||||
Exec(conn, tx, "INSERT OR IGNORE INTO _触发器开关 (id, 开关) VALUES (1, 0);");
|
||||
}
|
||||
|
||||
/// <summary>开关设值(1=批量写跳过触发器;0=编辑自动计算)。</summary>
|
||||
public static void 开关设(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx, int 值)
|
||||
{
|
||||
确保开关表(conn, tx);
|
||||
Exec(conn, tx, "UPDATE _触发器开关 SET 开关=" + 值 + " WHERE id=1;");
|
||||
}
|
||||
|
||||
/// <summary>刷新图号行聚合(箱号增删后调用;箱号表名按表索引查;一条 UPDATE,公式来自公式表)。</summary>
|
||||
public static void 刷新图号行聚合(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
|
||||
string 项目库路径, string 图号ID, string 图号表名)
|
||||
{
|
||||
string xhTbl = null;
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT 实际表名 FROM 表索引 WHERE 类型='箱号' AND 图号=@th LIMIT 1;", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@th", 图号ID);
|
||||
object o = cmd.ExecuteScalar();
|
||||
xhTbl = o == null || o == DBNull.Value ? null : o.ToString();
|
||||
}
|
||||
if (string.IsNullOrEmpty(xhTbl)) return;
|
||||
using (var fakConn = new System.Data.SQLite.SQLiteConnection(
|
||||
"Data Source=" + System.IO.Path.Combine(System.IO.Path.GetDirectoryName(项目库路径), "..", "fak.db") + ";Version=3;"))
|
||||
{
|
||||
fakConn.Open();
|
||||
var 图号公式 = 加载公式(fakConn, "图号");
|
||||
string thSet = 生成Set片段(图号公式, "ROW", xhTbl);
|
||||
Exec(conn, tx, "UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "';");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>新表(管线内建表后)即时补触发器——只处理该表自身。</summary>'''),
|
||||
], "计算引擎.cs")
|
||||
|
||||
# ============ ② 图号粘贴管线:抄源聚合值 + 开关包裹 ============
|
||||
p = ROOT + r"\主窗口.菜单与库.cs"
|
||||
edit(p, [
|
||||
# tx 后设开关=1(锚点:图号管线快照日志)
|
||||
(''' Log("[粘贴图号] 快照: 事务内图号数=" + 快照图号数 + " xhMax=" + xhMax + " yjMax=" + yjMax);''',
|
||||
''' Log("[粘贴图号] 快照: 事务内图号数=" + 快照图号数 + " xhMax=" + xhMax + " yjMax=" + yjMax);
|
||||
// ★ 批量模式:粘贴数据自带正确计算结果(源行快照),无需触发器逐行重算
|
||||
计算引擎.开关设(conn, tx, 1);'''),
|
||||
# 图号行 INSERT 抄源聚合值(原来是 0,0,0,0,0,0)
|
||||
(''' using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"INSERT INTO " + TuhaoTableName +
|
||||
" (序号, 图号, 台数, 箱柜数量, 单价, 总价, 成本总价, 毛利, 毛利率, 内部ID) " +
|
||||
"VALUES (@s, @th, @ts, 0, 0, 0, 0, 0, 0, @iid);", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@s", thSeqMax.ToString());
|
||||
cmd.Parameters.AddWithValue("@th", nt);
|
||||
cmd.Parameters.AddWithValue("@ts", GetSrcField(clip.TuhaoRow.Count > 0 ? clip.TuhaoRow[0] : null, "台数", "1"));
|
||||
cmd.Parameters.AddWithValue("@iid", nt);
|
||||
cmd.ExecuteNonQuery();
|
||||
}''',
|
||||
''' using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"INSERT INTO " + TuhaoTableName +
|
||||
" (序号, 图号, 台数, 箱柜数量, 单价, 总价, 成本总价, 毛利, 毛利率, 内部ID) " +
|
||||
"VALUES (@s, @th, @ts, @sl, @dj, @zj, @cbzj, @ml, @mll, @iid);", conn, tx))
|
||||
{
|
||||
// ★ 副本=源:聚合值直接抄源图号行(箱号/元件表是完整副本,聚合天然相等)
|
||||
var srcTh = clip.TuhaoRow.Count > 0 ? clip.TuhaoRow[0] : null;
|
||||
cmd.Parameters.AddWithValue("@s", thSeqMax.ToString());
|
||||
cmd.Parameters.AddWithValue("@th", nt);
|
||||
cmd.Parameters.AddWithValue("@ts", GetSrcField(srcTh, "台数", "1"));
|
||||
cmd.Parameters.AddWithValue("@sl", GetSrcFieldDecimal(srcTh, "箱柜数量", 0));
|
||||
cmd.Parameters.AddWithValue("@dj", GetSrcFieldDecimal(srcTh, "单价", 0));
|
||||
cmd.Parameters.AddWithValue("@zj", GetSrcFieldDecimal(srcTh, "总价", 0));
|
||||
cmd.Parameters.AddWithValue("@cbzj", GetSrcFieldDecimal(srcTh, "成本总价", 0));
|
||||
cmd.Parameters.AddWithValue("@ml", GetSrcFieldDecimal(srcTh, "毛利", 0));
|
||||
cmd.Parameters.AddWithValue("@mll", GetSrcFieldDecimal(srcTh, "毛利率", 0));
|
||||
cmd.Parameters.AddWithValue("@iid", nt);
|
||||
cmd.ExecuteNonQuery();
|
||||
}'''),
|
||||
# 箱号行 INSERT 抄源聚合值(原 7 个 0)
|
||||
(''' xhSeq++;
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"INSERT INTO \\"" + xhTbl + "\\" VALUES (@s, @xh, @xm, @model, @spec, @unit, @count," +
|
||||
" 0, 0, 0, 0, 0, 0, 0, @type, @remark, @drawing, @iid);", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@s", xhSeq.ToString());
|
||||
cmd.Parameters.AddWithValue("@xh", nx);
|
||||
cmd.Parameters.AddWithValue("@xm", GetSrcField(xhRow, "箱名", ""));
|
||||
cmd.Parameters.AddWithValue("@model", GetSrcField(xhRow, "型号", ""));
|
||||
cmd.Parameters.AddWithValue("@spec", GetSrcField(xhRow, "规格", ""));
|
||||
cmd.Parameters.AddWithValue("@unit", GetSrcField(xhRow, "单位", "台"));
|
||||
cmd.Parameters.AddWithValue("@count", GetSrcField(xhRow, "数量", "1"));
|
||||
cmd.Parameters.AddWithValue("@type", GetSrcField(xhRow, "箱柜类型", ""));
|
||||
cmd.Parameters.AddWithValue("@remark", GetSrcField(xhRow, "备注", ""));
|
||||
cmd.Parameters.AddWithValue("@drawing", GetSrcField(xhRow, "图纸", ""));
|
||||
cmd.Parameters.AddWithValue("@iid", nx);
|
||||
cmd.ExecuteNonQuery();
|
||||
}''',
|
||||
''' xhSeq++;
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"INSERT INTO \\"" + xhTbl + "\\" VALUES (@s, @xh, @xm, @model, @spec, @unit, @count," +
|
||||
" @dtcb, @cbj, @dtbc, @bcj, @lr, @lrl, @mgl, @type, @remark, @drawing, @iid);", conn, tx))
|
||||
{
|
||||
// ★ 副本=源:箱号行聚合值直接抄源行(单台成本/成本价/单台报出/报出价/利润/利润率/毛利率)
|
||||
cmd.Parameters.AddWithValue("@s", xhSeq.ToString());
|
||||
cmd.Parameters.AddWithValue("@xh", nx);
|
||||
cmd.Parameters.AddWithValue("@xm", GetSrcField(xhRow, "箱名", ""));
|
||||
cmd.Parameters.AddWithValue("@model", GetSrcField(xhRow, "型号", ""));
|
||||
cmd.Parameters.AddWithValue("@spec", GetSrcField(xhRow, "规格", ""));
|
||||
cmd.Parameters.AddWithValue("@unit", GetSrcField(xhRow, "单位", "台"));
|
||||
cmd.Parameters.AddWithValue("@count", GetSrcField(xhRow, "数量", "1"));
|
||||
cmd.Parameters.AddWithValue("@dtcb", GetSrcFieldDecimal(xhRow, "单台成本", 0));
|
||||
cmd.Parameters.AddWithValue("@cbj", GetSrcFieldDecimal(xhRow, "成本价", 0));
|
||||
cmd.Parameters.AddWithValue("@dtbc", GetSrcFieldDecimal(xhRow, "单台报出", 0));
|
||||
cmd.Parameters.AddWithValue("@bcj", GetSrcFieldDecimal(xhRow, "报出价", 0));
|
||||
cmd.Parameters.AddWithValue("@lr", GetSrcFieldDecimal(xhRow, "利润", 0));
|
||||
cmd.Parameters.AddWithValue("@lrl", GetSrcFieldDecimal(xhRow, "利润率", 0));
|
||||
cmd.Parameters.AddWithValue("@mgl", GetSrcFieldDecimal(xhRow, "毛利率", 0));
|
||||
cmd.Parameters.AddWithValue("@type", GetSrcField(xhRow, "箱柜类型", ""));
|
||||
cmd.Parameters.AddWithValue("@remark", GetSrcField(xhRow, "备注", ""));
|
||||
cmd.Parameters.AddWithValue("@drawing", GetSrcField(xhRow, "图纸", ""));
|
||||
cmd.Parameters.AddWithValue("@iid", nx);
|
||||
cmd.ExecuteNonQuery();
|
||||
}'''),
|
||||
# 管尾:开关=0(Commit 前)
|
||||
(''' using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT COUNT(*) FROM " + TuhaoTableName + ";", conn, tx))
|
||||
Log("[粘贴图号] Commit前(事务内): 图号数=" + cmd.ExecuteScalar());''',
|
||||
''' // ★ 恢复触发器(编辑自动计算)
|
||||
计算引擎.开关设(conn, tx, 0);
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT COUNT(*) FROM " + TuhaoTableName + ";", conn, tx))
|
||||
Log("[粘贴图号] Commit前(事务内): 图号数=" + cmd.ExecuteScalar());'''),
|
||||
# 箱号粘贴管线:开关包裹+管尾刷图号行
|
||||
(''' using (var conn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(dbPath)))
|
||||
{
|
||||
conn.Open();
|
||||
using (var tx = conn.BeginTransaction())
|
||||
{
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
string baseName = !string.IsNullOrWhiteSpace(clip.SrcXianghaoName) ? clip.SrcXianghaoName : "箱号";
|
||||
string newXhId = MakeUniqueName(baseName,
|
||||
candidate => NameExistsXhInConn2(conn, tx, th, candidate));
|
||||
|
||||
PasteXianghaoRowToDb(pid, th, newXhId, clip.XianghaoRow, conn, tx);
|
||||
PasteYuanjianRowsToDb(pid, th, newXhId, clip.YuanjianRows, conn, tx);
|
||||
pastedCount++;
|
||||
lastXhId = newXhId;
|
||||
}
|
||||
tx.Commit();
|
||||
}
|
||||
// ★ 触发器已自动算好箱号/图号聚合;只补跨库项目汇总
|
||||
计算引擎.刷新项目汇总(dbPath, pid, TuhaoTableName);''',
|
||||
''' using (var conn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(dbPath)))
|
||||
{
|
||||
conn.Open();
|
||||
using (var tx = conn.BeginTransaction())
|
||||
{
|
||||
// ★ 批量模式:粘贴行自带源计算结果;关触发器,提交前刷一次图号行聚合
|
||||
计算引擎.开关设(conn, tx, 1);
|
||||
foreach (var clip in clips)
|
||||
{
|
||||
string baseName = !string.IsNullOrWhiteSpace(clip.SrcXianghaoName) ? clip.SrcXianghaoName : "箱号";
|
||||
string newXhId = MakeUniqueName(baseName,
|
||||
candidate => NameExistsXhInConn2(conn, tx, th, candidate));
|
||||
|
||||
PasteXianghaoRowToDb(pid, th, newXhId, clip.XianghaoRow, conn, tx);
|
||||
PasteYuanjianRowsToDb(pid, th, newXhId, clip.YuanjianRows, conn, tx);
|
||||
pastedCount++;
|
||||
lastXhId = newXhId;
|
||||
}
|
||||
// 箱号行聚合抄源行已正确;仅图号行聚合(全箱号表 SUM)需刷新一次
|
||||
计算引擎.刷新图号行聚合(conn, tx, dbPath, th, TuhaoTableName);
|
||||
计算引擎.开关设(conn, tx, 0);
|
||||
tx.Commit();
|
||||
}
|
||||
// 只补跨库项目汇总
|
||||
计算引擎.刷新项目汇总(dbPath, pid, TuhaoTableName);'''),
|
||||
], "主窗口.菜单与库.cs")
|
||||
|
||||
print("合计", n, "处")
|
||||
@@ -0,0 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
||||
<path d="M14 6 H38 L50 18 V58 H14 Z" fill="#FFFFFF" stroke="#C0392B" stroke-width="3" stroke-linejoin="round"/>
|
||||
<path d="M38 6 V18 H50" fill="none" stroke="#C0392B" stroke-width="3" stroke-linejoin="round"/>
|
||||
<rect x="10" y="34" width="34" height="18" rx="3" fill="#D64541"/>
|
||||
<text x="27" y="48" font-family="Segoe UI, Arial" font-size="13" font-weight="bold" fill="#FFFFFF" text-anchor="middle" dominant-baseline="central">PDF</text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 510 B |
@@ -0,0 +1,142 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""加载卡顿优化:①计算引擎加公式指纹(PRAGMA user_version 比对,一致跳过重建)
|
||||
②EnsureFormulaTriggersQuiet 改后台+指纹跳过 ③三表 Reload 加 Stopwatch 耗时日志。幂等。"""
|
||||
import io
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
|
||||
n = 0
|
||||
|
||||
def edit(path, pairs, tag):
|
||||
global n
|
||||
s = io.open(path, encoding="utf-8-sig").read()
|
||||
for old, new in pairs:
|
||||
if old not in s:
|
||||
if new in s:
|
||||
print(tag, "已有(跳过)")
|
||||
continue
|
||||
raise AssertionError(tag + " 锚点缺失: " + old[:70])
|
||||
s = s.replace(old, new, 1)
|
||||
n += 1
|
||||
io.open(path, "w", encoding="utf-8-sig").write(s)
|
||||
print(tag, "ok")
|
||||
|
||||
# 1) 计算引擎:公式指纹
|
||||
edit(ROOT + r"\计算引擎.cs", [
|
||||
(''' /// <summary>加载某层级启用中的公式(按排序)。加载失败(表缺/异常)回退默认公式。</summary>''',
|
||||
''' /// <summary>公式表内容指纹(全部公式行串联的稳定 hash)。用于判断触发器是否需要重建。</summary>
|
||||
public static int 公式指纹(System.Data.SQLite.SQLiteConnection fakConn)
|
||||
{
|
||||
try
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT 层级,目标列,表达式,排序,启用 FROM \\"" + 公式表名 + "\\" ORDER BY 排序;", fakConn))
|
||||
using (var r = cmd.ExecuteReader())
|
||||
while (r.Read())
|
||||
sb.Append(r.GetValue(0)).Append('|').Append(r.GetValue(1)).Append('|')
|
||||
.Append(r.GetValue(2)).Append('|').Append(r.GetValue(3)).Append('|')
|
||||
.Append(r.GetValue(4)).Append(';');
|
||||
return sb.ToString().GetHashCode();
|
||||
}
|
||||
catch { return 0; }
|
||||
}
|
||||
|
||||
/// <summary>读取项目库 user_version(存的是上次重建时的公式指纹)。</summary>
|
||||
public static int 读触发器版本(System.Data.SQLite.SQLiteConnection conn)
|
||||
{
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand("PRAGMA user_version;", conn))
|
||||
return Convert.ToInt32(cmd.ExecuteScalar());
|
||||
}
|
||||
|
||||
/// <summary>写入项目库 user_version。</summary>
|
||||
public static void 写触发器版本(System.Data.SQLite.SQLiteConnection conn, int 版本)
|
||||
{
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand("PRAGMA user_version=" + 版本 + ";", conn))
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
/// <summary>加载某层级启用中的公式(按排序)。加载失败(表缺/异常)回退默认公式。</summary>'''),
|
||||
], "计算引擎.cs")
|
||||
|
||||
# 2) 编辑直写库.cs:指纹跳过+后台化
|
||||
edit(ROOT + r"\编辑直写库.cs", [
|
||||
(''' /// <summary>进项目/建表/撤销删除后:按公式表重建项目库全部触发器(幂等,安静模式只记日志)。</summary>
|
||||
private void EnsureFormulaTriggersQuiet(string projectId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(projectId)) return;
|
||||
string dbPath = GetQuoteDbPath(projectId);
|
||||
if (!System.IO.File.Exists(dbPath)) return;
|
||||
string fak = GetDbFullPath(@"sjk\\fak.db");
|
||||
using (var conn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(dbPath)))
|
||||
using (var fakConn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(fak)))
|
||||
{
|
||||
conn.Open();
|
||||
fakConn.Open();
|
||||
计算引擎.EnsureFormulaTable(fakConn);
|
||||
计算引擎.重建项目全部触发器(conn, null, TuhaoTableName, fakConn);
|
||||
}
|
||||
Log("[公式触发器] 已按公式表重建 [" + projectId + "] 全部触发器");
|
||||
}
|
||||
catch (Exception ex) { Log("[公式触发器] 重建失败(不影响编辑,但派生列不会自动算): " + ex.Message); }
|
||||
}''',
|
||||
''' /// <summary>进项目/建表/撤销删除后:按公式表重建项目库全部触发器。
|
||||
/// ★ 性能:公式指纹与项目库 user_version 一致时直接跳过(毫秒级);需要重建时放后台线程不卡 UI。</summary>
|
||||
private void EnsureFormulaTriggersQuiet(string projectId)
|
||||
{
|
||||
System.Threading.Tasks.Task.Run(delegate { EnsureFormulaTriggersCore(projectId); });
|
||||
}
|
||||
|
||||
private void EnsureFormulaTriggersCore(string projectId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(projectId)) return;
|
||||
string dbPath = GetQuoteDbPath(projectId);
|
||||
if (!System.IO.File.Exists(dbPath)) return;
|
||||
string fak = GetDbFullPath(@"sjk\\fak.db");
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
using (var conn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(dbPath)))
|
||||
using (var fakConn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(fak)))
|
||||
{
|
||||
conn.Open();
|
||||
fakConn.Open();
|
||||
计算引擎.EnsureFormulaTable(fakConn);
|
||||
int 指纹 = 计算引擎.公式指纹(fakConn);
|
||||
int 已存 = 计算引擎.读触发器版本(conn);
|
||||
if (指纹 != 0 && 指纹 == 已存)
|
||||
{
|
||||
Log("[公式触发器] 公式未变化,跳过重建 (" + projectId + ")");
|
||||
return;
|
||||
}
|
||||
计算引擎.重建项目全部触发器(conn, null, TuhaoTableName, fakConn);
|
||||
计算引擎.写触发器版本(conn, 指纹);
|
||||
}
|
||||
sw.Stop();
|
||||
Log("[公式触发器] 已按公式表重建 [" + projectId + "] 全部触发器, 耗时 " + sw.ElapsedMilliseconds + "ms");
|
||||
}
|
||||
catch (Exception ex) { Log("[公式触发器] 重建失败(不影响编辑,但派生列不会自动算): " + ex.Message); }
|
||||
}'''),
|
||||
], "编辑直写库.cs")
|
||||
|
||||
# 3) 三表 Reload 耗时日志
|
||||
p3 = ROOT + r"\主窗口.cs"
|
||||
edit(p3, [
|
||||
(''' _tuhaoTable = table; // 记下当前表引用,供关闭/切换时回写
|
||||
this.advancedDataGridView9.RowCount = table.Rows.Count;
|
||||
this.advancedDataGridView9.Invalidate();''',
|
||||
''' _tuhaoTable = table; // 记下当前表引用,供关闭/切换时回写
|
||||
this.advancedDataGridView9.RowCount = table.Rows.Count;
|
||||
this.advancedDataGridView9.Invalidate();
|
||||
Log("[性能] ReloadTuhaoByProject 耗时 " + _swReload.ElapsedMilliseconds + "ms (含取缓存/查库/转换)");'''),
|
||||
(''' _xianghaoTable = table; // 记下当前表引用,供切换/关闭时回写
|
||||
this.advancedDataGridView4.RowCount = table.Rows.Count;
|
||||
this.advancedDataGridView4.Invalidate();''',
|
||||
''' _xianghaoTable = table; // 记下当前表引用,供切换/关闭时回写
|
||||
this.advancedDataGridView4.RowCount = table.Rows.Count;
|
||||
this.advancedDataGridView4.Invalidate();
|
||||
Log("[性能] ReloadXianghao 耗时 " + _swReload.ElapsedMilliseconds + "ms (含取缓存/查库/转换)");'''),
|
||||
], "主窗口.cs")
|
||||
|
||||
print("合计", n, "处")
|
||||
@@ -0,0 +1,221 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""全量价格在线预热:遍历全树所有系列的所有规格组合,经本地旧 python 代理(8080,在线模式)
|
||||
逐个请求 webGetPrice 存入 proxy_cache;完成后重跑 migrate_price_cache.py 平移到新服务。
|
||||
|
||||
用法:先双击 启动旧代理预热数据.bat(联网),确认 http://localhost:8080/standalone 能开,
|
||||
然后: python tools/preheat_prices.py (全量,约 3-6 万请求,20-60 分钟)
|
||||
python tools/preheat_prices.py 1122 1506 (只预热指定系列,调试用)
|
||||
组合策略:每系列"全默认组合 + 每组每个选项单独变化"——覆盖换任一规格的真实查询主路径;
|
||||
可选 ALL=1 环境变量改全笛卡尔积(每系列上限 300)。
|
||||
(请求走 http.client 直连 127.0.0.1:8080 本地代理,不访问任何外部地址。)
|
||||
"""
|
||||
import base64
|
||||
import http.client
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
ZIPS = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "元件库网页", "data", "dian", "electricData_zips")
|
||||
ONLY = sys.argv[1:]
|
||||
全组合 = os.environ.get("ALL") == "1"
|
||||
HOST, PORT, PATH = "127.0.0.1", 8080, "/dx2/pcprice/default/dx2/interface/webGetPrice"
|
||||
|
||||
|
||||
def 全树系列():
|
||||
z = zipfile.ZipFile(os.path.join(ZIPS, "s.zip"))
|
||||
d = json.loads(z.read("s.js"))
|
||||
return [(x["a"], x["b"]) for x in d if x.get("c") == 1]
|
||||
|
||||
|
||||
def 在线拉ZIP(v逻辑名):
|
||||
"""经本地旧代理在线拉 electricData 包(自动缓存);返回 bytes(ZIP)。"""
|
||||
v = base64.b64encode(v逻辑名.encode()).decode().rstrip("=")
|
||||
try:
|
||||
conn = http.client.HTTPConnection(HOST, PORT, timeout=25)
|
||||
conn.request("GET", "/electricData?v=" + v + "&t=" + str(int(time.time() * 1000)))
|
||||
r = conn.getresponse()
|
||||
body = r.read()
|
||||
conn.close()
|
||||
return body if r.status == 200 and len(body) > 100 else None
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def 树系列(body):
|
||||
"""从树 ZIP bytes 解析叶子系列号(s 字段)列表。"""
|
||||
import io as _io
|
||||
try:
|
||||
z = zipfile.ZipFile(_io.BytesIO(body))
|
||||
d = json.loads(z.read(z.namelist()[0]))
|
||||
return [x["s"] for x in d if isinstance(x, dict) and x.get("s")]
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def 全部系列():
|
||||
"""f.zip=厂家列表(o=代表系列号=树id)→拉每厂树→汇总全部叶子系列号。"""
|
||||
z = zipfile.ZipFile(os.path.join(ZIPS, "f.zip"))
|
||||
f = json.loads(z.read(z.namelist()[0]))
|
||||
# ★ 树 id = f 字段(正泰=1 德力西=2,与 1.zip/2.zip 吻合);o 是代表系列号不是树 id
|
||||
厂家 = sorted(set(x["f"] for x in f
|
||||
if isinstance(x, dict) and x.get("f") not in (0, "0", None, "", False)),
|
||||
key=str)
|
||||
print("厂家树数:", len(厂家), flush=True)
|
||||
系列 = []
|
||||
拉树成功 = 0
|
||||
for i, o in enumerate(厂家):
|
||||
# 本地已有树的直接读
|
||||
zp = os.path.join(ZIPS, str(o) + ".zip")
|
||||
leaves = []
|
||||
if os.path.exists(zp):
|
||||
try:
|
||||
zz = zipfile.ZipFile(zp)
|
||||
leaves = 树系列(zz.read(zz.namelist()[0]))
|
||||
except Exception:
|
||||
pass
|
||||
if not leaves:
|
||||
body = 在线拉ZIP("z_seri/%s.jss" % o)
|
||||
if body:
|
||||
leaves = 树系列(body)
|
||||
time.sleep(0.1)
|
||||
if leaves:
|
||||
拉树成功 += 1
|
||||
系列.extend(leaves)
|
||||
if (i + 1) % 50 == 0:
|
||||
print(" 厂家进度 %d/%d, 已拉树 %d, 系列累计 %d" % (i + 1, len(厂家), 拉树成功, len(系列)), flush=True)
|
||||
去重 = sorted(set(str(x) for x in 系列))
|
||||
print("树拉取 %d/%d, 系列总数(去重) %d" % (拉树成功, len(厂家), len(去重)), flush=True)
|
||||
return [int(x) for x in 去重]
|
||||
|
||||
|
||||
def 在线拉系列ZIP(系列id):
|
||||
"""经本地旧代理在线拉系列规格包(z_seri/N.jss);返回解析出的 b 数组。"""
|
||||
body = 在线拉ZIP("z_seri/%s.jss" % 系列id)
|
||||
if body is None:
|
||||
return None
|
||||
import io as _io
|
||||
try:
|
||||
z = zipfile.ZipFile(_io.BytesIO(body))
|
||||
data = json.loads(z.read(z.namelist()[0]))
|
||||
return data.get("b", []) if isinstance(data, dict) else []
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def 规格组合(系列id):
|
||||
"""从系列 ZIP 的 b 数组解析规格组:ad=组号,ab=选项id。
|
||||
返回组合列表(选项连串,如 "337-350-15-9-29-30-37")。"""
|
||||
zp = os.path.join(ZIPS, str(系列id) + ".zip")
|
||||
b = None
|
||||
if os.path.exists(zp):
|
||||
z = zipfile.ZipFile(zp)
|
||||
inner = z.namelist()[0]
|
||||
data = json.loads(z.read(inner))
|
||||
data = None
|
||||
zp = os.path.join(ZIPS, str(系列id) + ".zip")
|
||||
import io as _io
|
||||
if os.path.exists(zp):
|
||||
try:
|
||||
data = json.loads(zipfile.ZipFile(zp).read(zipfile.ZipFile(zp).namelist()[0]))
|
||||
except Exception:
|
||||
data = None
|
||||
if data is None:
|
||||
body = 在线拉ZIP("z_seri/%s.jss" % 系列id)
|
||||
if body is None:
|
||||
return []
|
||||
try:
|
||||
data = json.loads(zipfile.ZipFile(_io.BytesIO(body)).read(
|
||||
zipfile.ZipFile(_io.BytesIO(body)).namelist()[0]))
|
||||
except Exception:
|
||||
return []
|
||||
# ★ 规格组=顶层 c 键里 bc==0 的项(组名 aj),选项=ar[].as;b 数组是附件树不是规格
|
||||
groups = {}
|
||||
c = data.get("c") if isinstance(data, dict) else None
|
||||
if not c:
|
||||
return []
|
||||
for 组 in c:
|
||||
if not isinstance(组, dict) or 组.get("bc") != 0:
|
||||
continue
|
||||
p_ = 组.get("p")
|
||||
ar = 组.get("ar") or []
|
||||
选项 = [o["as"] for o in ar if isinstance(o, dict) and o.get("as") not in (None, 0)]
|
||||
if p_ is not None and 选项:
|
||||
groups.setdefault(p_, [])
|
||||
for a in 选项:
|
||||
if a not in groups[p_]:
|
||||
groups[p_].append(a)
|
||||
if not groups:
|
||||
return []
|
||||
ads = sorted(groups)
|
||||
默认 = [str(groups[ad][0]) for ad in ads]
|
||||
组合 = ["-".join(默认)]
|
||||
for i, ad in enumerate(ads):
|
||||
for ab in groups[ad][1:]:
|
||||
c = list(默认)
|
||||
c[i] = str(ab)
|
||||
组合.append("-".join(c))
|
||||
if 全组合:
|
||||
import itertools
|
||||
allc = ["-".join(p) for p in itertools.product(*[[str(a) for a in groups[ad]] for ad in ads])]
|
||||
组合 = allc[:300]
|
||||
return 组合
|
||||
|
||||
|
||||
def 查价(系列id, 组合):
|
||||
参数 = {"s": str(系列id), "options": [组合], "time": int(time.time() * 1000), "from": "dx2jsapi"}
|
||||
info = base64.b64encode(json.dumps(参数, ensure_ascii=False).encode("utf-8")).decode()
|
||||
try:
|
||||
conn = http.client.HTTPConnection(HOST, PORT, timeout=15)
|
||||
conn.request("GET", PATH + "?infoJson=" + info)
|
||||
r = conn.getresponse()
|
||||
body = r.read()
|
||||
conn.close()
|
||||
ok = b'"code": 0' in body or b'"code":0' in body
|
||||
return len(body), ok
|
||||
except Exception as e:
|
||||
return -1, str(e)[:50]
|
||||
|
||||
|
||||
def main():
|
||||
if ONLY:
|
||||
系列 = [int(o) for o in ONLY]
|
||||
print("指定系列数:", len(系列))
|
||||
else:
|
||||
系列 = 全部系列() # f.zip 厂家→每厂树→全部叶子系列号
|
||||
import threading
|
||||
lock = threading.Lock()
|
||||
stat = [0, 0, 0] # 序列idx, 总, 成功
|
||||
|
||||
def 处理(sid):
|
||||
组合 = 规格组合(sid)
|
||||
局部总 = 局部成 = 0
|
||||
for c in 组合:
|
||||
n, ok = 查价(sid, c)
|
||||
局部总 += 1
|
||||
if ok:
|
||||
局部成 += 1
|
||||
time.sleep(0.01)
|
||||
with lock:
|
||||
stat[0] += 1
|
||||
stat[1] += 局部总
|
||||
stat[2] += 局部成
|
||||
if stat[0] % 100 == 0:
|
||||
速率 = stat[1] / max(1, time.time() - t0)
|
||||
print("[%d/%d] 系列%s: %d组合 累计%d 成功%d (%.1f/s)" %
|
||||
(stat[0], len(系列), sid, len(组合), stat[1], stat[2], 速率), flush=True)
|
||||
|
||||
t0 = time.time()
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
with ThreadPoolExecutor(max_workers=4) as pool:
|
||||
list(pool.map(处理, 系列))
|
||||
总, 成功 = stat[1], stat[2]
|
||||
print()
|
||||
print("完成: 请求 %d, 成功 %d, 用时 %.0f 分钟" % (总, 成功, (time.time() - t0) / 60))
|
||||
print("下一步: python tools/migrate_price_cache.py 平移到新服务")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,252 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""①把 MenuTuhao_AddToFak_Click 的复制逻辑抽成 复制图号到方案库(可复用)
|
||||
②Click 改为调用 ③新增项目表右键"加入方案库"(整项目) ④挂右键菜单。幂等。"""
|
||||
import io
|
||||
import re
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\主窗口.菜单与库.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
n = 0
|
||||
|
||||
def rep(old, new, tag):
|
||||
global s, n
|
||||
if old not in s:
|
||||
if new in s:
|
||||
print(tag, "跳过")
|
||||
return
|
||||
raise AssertionError(tag + " 缺失: " + old[:70])
|
||||
s = s.replace(old, new, 1)
|
||||
n += 1
|
||||
print(tag, "ok")
|
||||
|
||||
# ---- 定位 MenuTuhao_AddToFak_Click 方法体 ----
|
||||
start_anchor = " private void MenuTuhao_AddToFak_Click(object sender, EventArgs e)"
|
||||
i0 = s.find(start_anchor)
|
||||
assert i0 > 0, "AddToFak 方法头缺失"
|
||||
# 方法结束:其后第一个 \n }\n (8空格闭括号=方法级)
|
||||
m_end = re.compile(r"\n \}\r?\n").search(s, i0)
|
||||
end = m_end.end()
|
||||
body = s[i0:end]
|
||||
print("AddToFak 方法体长度:", len(body))
|
||||
|
||||
# ---- 1) 生成 复制图号到方案库 方法:参数化 _currentTuhao→图号内部ID ----
|
||||
core = body
|
||||
# 换方法名与签名
|
||||
core = core.replace(
|
||||
" private void MenuTuhao_AddToFak_Click(object sender, EventArgs e)\n {",
|
||||
" /// <summary>复制一个图号(含箱号/元件子树)从项目库到方案库。供单图号加入与整项目加入共用。</summary>\n"
|
||||
" private void 复制图号到方案库(string projectId, string 图号内部ID, string srcDbPath, string fakDbPath)\n"
|
||||
" {", 1)
|
||||
# 参数化 _currentTuhao
|
||||
core = core.replace("_currentTuhao", "图号内部ID")
|
||||
# 删 Click 专属校验段(从开头到 fakDbPath 定义前)
|
||||
m_fak = re.search(r"(\n\s*string fakDbPath = GetDbFullPath\(@\"sjk\\\\fak\.db\"\);)", core)
|
||||
if not m_fak:
|
||||
m_fak = re.search(r"(\n\s*string fakDbPath = GetDbFullPath\(.+fak\.db\"\);)", core)
|
||||
assert m_fak, "fakDbPath 行未找到"
|
||||
# ★ 保留新方法签名(否则切割会把它连同校验段一起丢掉)
|
||||
new_sig = (" /// <summary>复制一个图号(含箱号/元件子树)从项目库到方案库。供单图号加入与整项目加入共用。</summary>\n"
|
||||
" private void 复制图号到方案库(string projectId, string 图号内部ID, string srcDbPath, string fakDbPath)\n"
|
||||
" {")
|
||||
core = new_sig + "\n" + core[m_fak.start(1):] # 签名 + 从 fakDbPath 定义开始保留
|
||||
# 删尾部 ReloadFakData + MessageBox(调用方负责)
|
||||
core = re.sub(r"\n\s*// 刷新方案页面的图号表\r?\n\s*ReloadFakData\(\);\r?\n", "\n", core)
|
||||
core = re.sub(r"\n\s*string msg = \"已把图号[^\r\n]*\r?\n(?:\s+[^\r\n]*\r?\n)*?\s*MessageBox\.Show\(this, msg, \"加入方案库\",[^;]*;\r?\n", "\n", core)
|
||||
# 内部 xhCount/yjCount 声明保留;统计行保留
|
||||
new_method = core
|
||||
|
||||
# ---- 2) 原 Click 改为调方法 ----
|
||||
new_click = ''' private void MenuTuhao_AddToFak_Click(object sender, EventArgs e)
|
||||
{
|
||||
Log("点击[加入方案库], _currentProjectId=[" + _currentProjectId +
|
||||
"] _currentTuhao=[" + _currentTuhao + "]");
|
||||
|
||||
// 1) 校验当前选中状态
|
||||
if (string.IsNullOrWhiteSpace(_currentProjectId))
|
||||
{
|
||||
MessageBox.Show(this, "请先在项目表中双击选择一个项目,再加入方案库。",
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(_currentTuhao))
|
||||
{
|
||||
MessageBox.Show(this, "请先在图号表中选择要加入方案库的图号。",
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) 先保存当前三个表的未提交编辑,确保复制到方案库的是最新数据
|
||||
try { SaveAllQuoteTablesIfDirty(false); }
|
||||
catch (Exception exSave) { Log("[加入方案库] 预保存失败(继续): " + exSave.Message); }
|
||||
|
||||
string srcDbPath = GetQuoteDbPath(_currentProjectId);
|
||||
if (!System.IO.File.Exists(srcDbPath))
|
||||
{
|
||||
MessageBox.Show(this, "项目报价库不存在:\\n" + srcDbPath,
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
string fakDbPath = GetDbFullPath(@"sjk\\fak.db");
|
||||
|
||||
try
|
||||
{
|
||||
复制图号到方案库(_currentProjectId, _currentTuhao, srcDbPath, fakDbPath);
|
||||
Log("[加入方案库] 成功");
|
||||
ReloadFakData();
|
||||
MessageBox.Show(this, "已把图号 [" + _currentTuhao + "] 加入方案库。", "加入方案库",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
catch (Exception exAdd)
|
||||
{
|
||||
Log("[加入方案库] 失败: " + exAdd);
|
||||
MessageBox.Show(this, "加入方案库失败:\\n" + exAdd.Message, "加入方案库",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
}'''
|
||||
|
||||
s = s[:i0] + new_click + "\n" + new_method + s[end:]
|
||||
n += 1
|
||||
print("AddToFak 抽方法重构 ok")
|
||||
|
||||
# ---- 3) 新增 项目表右键"加入方案库"(整项目) ----
|
||||
proj_click = '''
|
||||
/// <summary>项目表右键「加入方案库」:把整个项目(全部图号+箱号+元件+项目信息)复制到方案库。</summary>
|
||||
private void MenuProject_AddToFak_Click(object sender, EventArgs e)
|
||||
{
|
||||
// 取当前选中项目(项目表当前行)
|
||||
var row = GetFirstSelectedRow(this.advancedDataGridView6);
|
||||
if (row == null)
|
||||
{
|
||||
MessageBox.Show(this, "请先在项目列表中选择要加入方案库的项目。",
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
string projectId = GetBoundValueAsString(row, "项目编号");
|
||||
if (string.IsNullOrWhiteSpace(projectId))
|
||||
{
|
||||
MessageBox.Show(this, "选中行没有项目编号,无法加入方案库。",
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
string srcDbPath = GetQuoteDbPath(projectId);
|
||||
if (!System.IO.File.Exists(srcDbPath))
|
||||
{
|
||||
MessageBox.Show(this, "项目报价库不存在:\\n" + srcDbPath,
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
string fakDbPath = GetDbFullPath(@"sjk\\fak.db");
|
||||
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
Cursor = Cursors.WaitCursor;
|
||||
try
|
||||
{
|
||||
// 1) 该项目的全部图号(内部ID,按序号顺序)
|
||||
var 图号们 = new System.Collections.Generic.List<string>();
|
||||
using (var conn = new System.Data.SQLite.SQLiteConnection(GetConnString(srcDbPath)))
|
||||
{
|
||||
conn.Open();
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT 内部ID FROM " + TuhaoTableName + " ORDER BY CAST(序号 AS INTEGER);", conn))
|
||||
using (var r = cmd.ExecuteReader())
|
||||
while (r.Read())
|
||||
{
|
||||
if (!r.IsDBNull(0))
|
||||
{
|
||||
string v = r.GetString(0);
|
||||
if (!string.IsNullOrEmpty(v)) 图号们.Add(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (图号们.Count == 0)
|
||||
{
|
||||
MessageBox.Show(this, "项目 [" + projectId + "] 没有图号数据,无法加入方案库。",
|
||||
"加入方案库", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) 逐图号复制到方案库
|
||||
foreach (var thId in 图号们)
|
||||
复制图号到方案库(projectId, thId, srcDbPath, fakDbPath);
|
||||
|
||||
// 3) 项目信息:该项目库的项目信息行覆盖 fak 的项目信息(项目页客户列显示用)
|
||||
string projTbl = ProjectInfoTableName;
|
||||
using (var srcConn = new System.Data.SQLite.SQLiteConnection(GetConnString(srcDbPath)))
|
||||
using (var dstConn = new System.Data.SQLite.SQLiteConnection(GetConnString(fakDbPath)))
|
||||
{
|
||||
srcConn.Open();
|
||||
dstConn.Open();
|
||||
var infoRow = new DataTable();
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT * FROM " + projTbl + " WHERE 项目编号=@id LIMIT 1;", srcConn))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@id", projectId);
|
||||
using (var ad = new System.Data.SQLite.SQLiteDataAdapter(cmd)) ad.Fill(infoRow);
|
||||
}
|
||||
var cols = new System.Collections.Generic.List<string>();
|
||||
foreach (DataColumn c in infoRow.Columns) cols.Add(c.ColumnName);
|
||||
using (var tx = dstConn.BeginTransaction())
|
||||
{
|
||||
Exec(dstConn, tx, "DELETE FROM " + projTbl + ";");
|
||||
foreach (DataRow r in infoRow.Rows)
|
||||
InsertRowByColumns(r, projTbl, cols, dstConn, tx);
|
||||
tx.Commit();
|
||||
}
|
||||
}
|
||||
|
||||
sw.Stop();
|
||||
Log("[加入方案库] 整项目完成: 项目=" + projectId + " 图号=" + 图号们.Count + " 耗时 " + sw.ElapsedMilliseconds + "ms");
|
||||
ReloadFakData();
|
||||
MessageBox.Show(this, "已把项目 [" + projectId + "] 加入方案库。\\n共 " + 图号们.Count + " 个图号,耗时 " +
|
||||
sw.ElapsedMilliseconds + "ms。", "加入方案库",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
catch (Exception exAdd)
|
||||
{
|
||||
Log("[加入方案库] 整项目失败: " + exAdd);
|
||||
MessageBox.Show(this, "加入方案库失败:\\n" + exAdd.Message, "加入方案库",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cursor = Cursors.Default;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Exec 简写(删除表数据用)。</summary>
|
||||
private static void Exec(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx, string sql)
|
||||
{
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(sql, conn, tx))
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
|
||||
'''
|
||||
|
||||
# ---- 插入 proj_click 到 菜单与库.cs(在 复制图号到方案库 方法之前) ----
|
||||
anchor = " /// <summary>复制一个图号(含箱号/元件子树)从项目库到方案库。供单图号加入与整项目加入共用。</summary>"
|
||||
print("anchor in s:", anchor in s, "| find:", s.find(anchor)); pos = s.find(anchor)
|
||||
assert pos > 0, "插入锚缺失"
|
||||
s = s[:pos] + proj_click + "\n" + s[pos:]
|
||||
n += 1
|
||||
print("项目右键方法插入 ok")
|
||||
|
||||
# ---- 4) 项目表右键菜单注册(MainForm.cs 挂方案菜单附近)——项目表无右键菜单,新建
|
||||
p_main = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\MainForm.cs"
|
||||
s4 = io.open(p_main, encoding="utf-8-sig").read()
|
||||
old4 = ''' // ===== 方案页面(tabPage13)三个表格的联动 ====='''
|
||||
new4 = ''' // ===== 项目表右键:加入方案库(整项目) =====
|
||||
var _menuProject = new ContextMenuStrip();
|
||||
_menuProject.Items.Add("加入方案库", null, MenuProject_AddToFak_Click);
|
||||
this.advancedDataGridView6.ContextMenuStrip = _menuProject;
|
||||
Log(" 项目表菜单已挂到 advancedDataGridView6(加入方案库)");
|
||||
|
||||
// ===== 方案页面(tabPage13)三个表格的联动 ====='''
|
||||
assert old4 in s4
|
||||
io.open(p_main, "w", encoding="utf-8-sig").write(s4.replace(old4, new4, 1))
|
||||
n += 1
|
||||
print("右键菜单注册 ok")
|
||||
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("合计", n, "处")
|
||||
@@ -0,0 +1,127 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""pdfbt Form3 列定义重建为标准设计器格式(57 字段+new+属性块+AddRange),设计器可见中文列头。
|
||||
列属性数据从 cadbt Form3.ui.cs 索引段提取(权威参照);行内容定位插入(缩进无关)。"""
|
||||
import io
|
||||
import re
|
||||
|
||||
CADBT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\cadbt\cadbt\Plugin\Form3.ui.cs"
|
||||
PDFBT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\pdfbt\Plugin\Form3.Designer.cs"
|
||||
TABLES = ["advancedDataGridView6", "advancedDataGridView9", "advancedDataGridView4", "advancedDataGridView5"]
|
||||
|
||||
cs = io.open(CADBT, encoding="utf-8-sig").read()
|
||||
ps = io.open(PDFBT, encoding="utf-8-sig").read()
|
||||
|
||||
# ---------- 1) 从 cadbt 解析每表每列属性 ----------
|
||||
pat_idx = re.compile(r"this\.(\w+)\.Columns\[(\d+)\]\.(\w+) = (.*?);")
|
||||
cols = {t: {} for t in TABLES}
|
||||
pat_add = re.compile(r"this\.(\w+)\.Columns\.Add\(new System\.Windows\.Forms\.DataGridViewTextBoxColumn\(\)\);")
|
||||
for l in cs.split("\n"):
|
||||
t = l.strip()
|
||||
m = pat_add.match(t)
|
||||
if m and m.group(1) in TABLES:
|
||||
continue
|
||||
m = pat_idx.match(t)
|
||||
if m and m.group(1) in cols:
|
||||
tbl, idx, prop, val = m.group(1), int(m.group(2)), m.group(3), m.group(4)
|
||||
cols[tbl].setdefault(idx, {})[prop] = val
|
||||
|
||||
for t in TABLES:
|
||||
n = len(cols[t])
|
||||
assert n > 0 and cols[t].get(0, {}).get("HeaderText"), t + " 解析不完整"
|
||||
print(t, "解析列数", n)
|
||||
|
||||
# ---------- 2) 生成标准设计器代码 ----------
|
||||
gnum = 0
|
||||
fields, news, blocks = [], [], []
|
||||
addRanges = {}
|
||||
|
||||
for tbl in TABLES:
|
||||
ids = []
|
||||
for idx in sorted(cols[tbl].keys()):
|
||||
gnum += 1
|
||||
fname = "dataGridViewTextBoxColumn%d" % gnum
|
||||
ids.append(fname)
|
||||
fields.append("\t\tprivate System.Windows.Forms.DataGridViewTextBoxColumn %s;" % fname)
|
||||
news.append("\t\t\tthis.%s = new System.Windows.Forms.DataGridViewTextBoxColumn();" % fname)
|
||||
props = dict(cols[tbl][idx])
|
||||
defaults = {"Width": "100", "Visible": "true", "ReadOnly": "true", "Frozen": "false",
|
||||
"AutoSizeMode": "System.Windows.Forms.DataGridViewAutoSizeColumnMode.NotSet",
|
||||
"SortMode": "System.Windows.Forms.DataGridViewColumnSortMode.NotSortable"}
|
||||
for k, v in defaults.items():
|
||||
props.setdefault(k, v)
|
||||
blocks.append("\t\t\t// ")
|
||||
blocks.append("\t\t\t// %s" % fname)
|
||||
blocks.append("\t\t\t// ")
|
||||
blocks.append('\t\t\tthis.%s.Name = "%s";' % (fname, fname))
|
||||
for k in ["HeaderText", "Width", "Visible", "ReadOnly", "Frozen", "AutoSizeMode",
|
||||
"DataPropertyName", "SortMode"]:
|
||||
if k in props:
|
||||
blocks.append("\t\t\tthis.%s.%s = %s;" % (fname, k, props[k]))
|
||||
inner = ",\n".join("\t\t\t\tthis." + i for i in ids)
|
||||
addRanges[tbl] = ("\t\t\tthis.%s.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] {\n" % tbl
|
||||
+ inner + "\n\t\t\t});")
|
||||
|
||||
# ---------- 3) 重写 pdfbt ----------
|
||||
|
||||
def lines_of(text):
|
||||
return text.split("\n")
|
||||
|
||||
def to_text(lines):
|
||||
return "\n".join(lines)
|
||||
|
||||
# 3a) 删索引式段
|
||||
for tbl in TABLES:
|
||||
pat_a = re.compile(r"this\." + tbl + r"\.Columns\.Add\(")
|
||||
pat_i = re.compile(r"this\." + tbl + r"\.Columns\[\d+\]\.")
|
||||
out, in_seg, seen = [], False, False
|
||||
for l in lines_of(ps):
|
||||
t = l.strip()
|
||||
if pat_a.search(t):
|
||||
in_seg, seen = True, True
|
||||
continue
|
||||
if in_seg:
|
||||
if pat_i.search(t) or t == "":
|
||||
continue
|
||||
in_seg = False
|
||||
out.append(l)
|
||||
assert seen, tbl + " 索引段未找到"
|
||||
ps = to_text(out)
|
||||
print(tbl, "索引段已删")
|
||||
|
||||
# 3b) 每表 ColumnHeadersDefaultCellStyle 行后插 AddRange
|
||||
for tbl in TABLES:
|
||||
ls = lines_of(ps)
|
||||
key = "this.%s.ColumnHeadersDefaultCellStyle" % tbl
|
||||
done = False
|
||||
for i, l in enumerate(ls):
|
||||
if key in l:
|
||||
ls.insert(i + 1, addRanges[tbl])
|
||||
done = True
|
||||
break
|
||||
assert done, tbl + " 样式行未找到"
|
||||
ps = to_text(ls)
|
||||
|
||||
# 3c) new 行插在 InitializeComponent 里第一个 cellStyle 声明行前
|
||||
ls = lines_of(ps)
|
||||
done = False
|
||||
for i, l in enumerate(ls):
|
||||
if "System.Windows.Forms.DataGridViewCellStyle" in l and "= new" in l:
|
||||
ls[i:i] = news
|
||||
done = True
|
||||
break
|
||||
assert done, "cellStyle 首行未找到"
|
||||
ps = to_text(ls)
|
||||
|
||||
# 3d) 字段声明插在 components = null 行后
|
||||
ls = lines_of(ps)
|
||||
done = False
|
||||
for i, l in enumerate(ls):
|
||||
if "private System.ComponentModel.IContainer components = null;" in l:
|
||||
ls[i + 1:i + 1] = fields
|
||||
done = True
|
||||
break
|
||||
assert done, "components 声明未找到"
|
||||
ps = to_text(ls)
|
||||
|
||||
io.open(PDFBT, "w", encoding="utf-8-sig").write(ps)
|
||||
print("重建完成: 字段%d new%d HeaderText%d" % (len(fields), len(news), len(re.findall("HeaderText", ps))))
|
||||
@@ -0,0 +1,169 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""触发器生成器重构:①去掉聚合用列引用模式 ②箱号表数量触发器按箱号行生成(WHEN 行ID,真 SUM)
|
||||
③图号行触发器传箱号表 ④重建/新表入口同步。幂等。"""
|
||||
import io
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\计算引擎.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
n = 0
|
||||
|
||||
def rep(old, new):
|
||||
global s, n
|
||||
if new in s and old not in s:
|
||||
print("已改(跳过):", old[:50].replace("\n", " "))
|
||||
return
|
||||
assert old in s, "锚点缺失: " + old[:80]
|
||||
s = s.replace(old, new, 1)
|
||||
n += 1
|
||||
|
||||
# 1) 子翻译 透传清理
|
||||
rep("p.引用模式 = 引用模式; p.下层表 = 下层表; p.层内公式 = 层内公式; p.展开栈 = 展开栈; p.聚合用列引用 = 聚合用列引用;",
|
||||
"p.引用模式 = 引用模式; p.下层表 = 下层表; p.层内公式 = 层内公式; p.展开栈 = 展开栈;")
|
||||
|
||||
# 2) 解析器删字段
|
||||
rep(''' public string 引用模式 = "NEW"; // NEW=触发器 NEW."列" / ROW=上级目标行 "列"
|
||||
public bool 聚合用列引用 = false; // true=SUM('列') 直接引用本行列(该聚合由下级触发器维护)''',
|
||||
''' public string 引用模式 = "NEW"; // NEW=触发器 NEW."列" / ROW=上级目标行 "列"''')
|
||||
|
||||
# 3) Sum子查询 删分支
|
||||
rep(''' /// <summary>SUM('列') → 对下层表整表求和;或(自触发场景)直接引用本行列(由下级触发器维护)。</summary>
|
||||
private string Sum子查询(string col)
|
||||
{
|
||||
string c = "\\"" + col.Replace("\\"", "\\"\\"") + "\\"";
|
||||
if (聚合用列引用)
|
||||
return "COALESCE(CAST(" + (引用模式 == "NEW" ? "NEW." : "") + c + " AS REAL),0)";
|
||||
return "COALESCE((SELECT CAST(SUM(CAST(" + c + " AS REAL)) AS REAL) FROM \\"" + 下层表.Replace("\\"", "\\"\\"") + "\\"),0)";
|
||||
}''',
|
||||
''' /// <summary>SUM('列') → 对下层表整表求和(每箱号一张元件表/每图号一张箱号表,无条件)。</summary>
|
||||
private string Sum子查询(string col)
|
||||
{
|
||||
return "COALESCE((SELECT CAST(SUM(CAST(\\"" + col.Replace("\\"", "\\"\\"") +
|
||||
"\\" AS REAL)) AS REAL) FROM \\"" + 下层表.Replace("\\"", "\\"\\"") + "\\"),0)";
|
||||
}''')
|
||||
|
||||
# 4) 翻译公式 删参数
|
||||
rep(''' private static string 翻译公式(string 表达式, Dictionary<string, 公式条目> 层内公式,
|
||||
string 引用模式, string 下层表, out string 错误, bool 聚合用列引用 = false)''',
|
||||
''' private static string 翻译公式(string 表达式, Dictionary<string, 公式条目> 层内公式,
|
||||
string 引用模式, string 下层表, out string 错误)''')
|
||||
rep(''' { 引用模式 = 引用模式, 下层表 = 下层表, 层内公式 = 层内公式, 展开栈 = new HashSet<string>(), 聚合用列引用 = 聚合用列引用 };''',
|
||||
''' { 引用模式 = 引用模式, 下层表 = 下层表, 层内公式 = 层内公式, 展开栈 = new HashSet<string>() };''')
|
||||
|
||||
# 5) 生成Set片段 删参数
|
||||
rep("private static string 生成Set片段(List<公式条目> 公式, string 引用模式, string 下层表, bool 聚合用列引用 = false)",
|
||||
"private static string 生成Set片段(List<公式条目> 公式, string 引用模式, string 下层表)")
|
||||
rep("string sql = 翻译公式(f.表达式, map, 引用模式, 下层表, out err, 聚合用列引用);",
|
||||
"string sql = 翻译公式(f.表达式, map, 引用模式, 下层表, out err);")
|
||||
rep("{ string e2; sql = 翻译公式(d[2], map, 引用模式, 下层表, out e2, 聚合用列引用); break; }",
|
||||
"{ string e2; sql = 翻译公式(d[2], map, 引用模式, 下层表, out e2); break; }")
|
||||
|
||||
# 6) 建箱号表触发器:按行生成
|
||||
rep(''' /// <summary>为一张箱号表生成触发器(u/i/d):行内公式 + 所属图号行聚合。</summary>
|
||||
public static void 建箱号表触发器(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
|
||||
string 箱号表, string 图号ID, string 图号表名,
|
||||
List<公式条目> 箱号公式, List<公式条目> 图号公式)
|
||||
{
|
||||
Drop触发器(conn, tx, 箱号表);
|
||||
string of = "";
|
||||
foreach (var c in 箱号输入列) of += "\\"" + c + "\\",";
|
||||
of = of.TrimEnd(',');
|
||||
|
||||
string xhSetNew = 生成Set片段(箱号公式, "NEW", null, true); // 本行(数量变);单台成本等由元件触发器维护
|
||||
string xhSetRow = 生成Set片段(箱号公式, "ROW", 箱号表, true); // 同上
|
||||
string thSet = 生成Set片段(图号公式, "ROW", 箱号表);
|
||||
string 刷图号 = "UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "';";
|
||||
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_u AFTER UPDATE OF " + of + " ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew +
|
||||
" WHERE rowid=NEW.rowid; " + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetRow +
|
||||
" WHERE rowid=NEW.rowid; " + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");
|
||||
}''',
|
||||
''' /// <summary>
|
||||
/// 为一张箱号表生成触发器。箱号聚合依赖"每行各自的元件表",数量更新触发器按行生成
|
||||
/// (WHEN NEW."内部ID"=行ID,体内 SUM 子查询 FROM 该行的元件表);INSERT/DELETE 表级只刷图号聚合。
|
||||
/// 行列表:每项 [箱号ID, 该行元件表名]。
|
||||
/// </summary>
|
||||
public static void 建箱号表触发器(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
|
||||
string 箱号表, string 图号ID, string 图号表名, List<string[]> 行列表,
|
||||
List<公式条目> 箱号公式, List<公式条目> 图号公式)
|
||||
{
|
||||
Drop触发器(conn, tx, 箱号表);
|
||||
string of = "";
|
||||
foreach (var c in 箱号输入列) of += "\\"" + c + "\\",";
|
||||
of = of.TrimEnd(',');
|
||||
|
||||
string thSet = 生成Set片段(图号公式, "ROW", 箱号表);
|
||||
string 刷图号 = "UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "';";
|
||||
|
||||
foreach (var 行 in 行列表)
|
||||
{
|
||||
string 行ID = 行[0], yj表 = 行[1];
|
||||
if (string.IsNullOrEmpty(yj表)) continue;
|
||||
string xhSetNew = 生成Set片段(箱号公式, "NEW", yj表);
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(行ID) +
|
||||
"_u AFTER UPDATE OF " + of + " ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" +
|
||||
行ID.Replace("'", "''") + "' BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew +
|
||||
" WHERE rowid=NEW.rowid; " + 刷图号 + "END;");
|
||||
}
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");
|
||||
}''')
|
||||
|
||||
# 7) 建图号行触发器:真 SUM
|
||||
rep(''' public static void 建图号行触发器(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
|
||||
string 图号表名, string 图号ID, List<公式条目> 图号公式)
|
||||
{
|
||||
string thSetNew = 生成Set片段(图号公式, "NEW", null, true);''',
|
||||
''' public static void 建图号行触发器(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
|
||||
string 图号表名, string 图号ID, string 箱号表, List<公式条目> 图号公式)
|
||||
{
|
||||
string thSetNew = 生成Set片段(图号公式, "NEW", 箱号表);''')
|
||||
|
||||
# 8) 重建项目全部触发器:组装行列表
|
||||
rep(''' // 先建箱号表映射后建元件触发器(元件触发器引用箱号表名,顺序无碍;图号行触发器每图号一次)
|
||||
var 已建图号 = new HashSet<string>();
|
||||
foreach (var m in 箱号行列表)
|
||||
建箱号表触发器(conn, tx, m[0], m[1], 图号表名, 箱号公式, 图号公式);
|
||||
foreach (var m in 元件表列表)
|
||||
建元件表触发器(conn, tx, m[0], m[1], m[2], m[3], 图号表名, 元件公式, 箱号公式, 图号公式);
|
||||
foreach (var kv in 箱号表Of图号)
|
||||
if (已建图号.Add(kv.Key))
|
||||
建图号行触发器(conn, tx, 图号表名, kv.Key, 图号公式);''',
|
||||
''' // 按箱号表分组元件表:[箱号表] → [箱号ID, 元件表] 列表
|
||||
var 行按表 = new Dictionary<string, List<string[]>>();
|
||||
foreach (var m in 元件表列表)
|
||||
{
|
||||
List<string[]> rows;
|
||||
if (!行按表.TryGetValue(m[1], out rows)) { rows = new List<string[]>(); 行按表[m[1]] = rows; }
|
||||
rows.Add(new[] { m[2], m[0] });
|
||||
}
|
||||
var 已建图号 = new HashSet<string>();
|
||||
foreach (var m in 箱号行列表)
|
||||
{
|
||||
List<string[]> rows;
|
||||
if (!行按表.TryGetValue(m[0], out rows)) rows = new List<string[]>();
|
||||
建箱号表触发器(conn, tx, m[0], m[1], 图号表名, rows, 箱号公式, 图号公式);
|
||||
}
|
||||
foreach (var m in 元件表列表)
|
||||
建元件表触发器(conn, tx, m[0], m[1], m[2], m[3], 图号表名, 元件公式, 箱号公式, 图号公式);
|
||||
foreach (var kv in 箱号表Of图号)
|
||||
if (已建图号.Add(kv.Key))
|
||||
建图号行触发器(conn, tx, 图号表名, kv.Key, kv.Value, 图号公式);''')
|
||||
|
||||
# 9) 建新表触发器:箱号分支传空行列表
|
||||
rep(''' else if (新表类型 == "箱号")
|
||||
建箱号表触发器(conn, tx, 箱号表, 图号ID, 图号表名, 箱号公式, 图号公式);''',
|
||||
''' else if (新表类型 == "箱号")
|
||||
{
|
||||
// 新箱号表暂无行级元件表映射;行级触发器由"建元件表触发器时同批重建/进项目自检"补齐
|
||||
建箱号表触发器(conn, tx, 箱号表, 图号ID, 图号表名, new List<string[]>(), 箱号公式, 图号公式);
|
||||
}''')
|
||||
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("重构完成", n, "处")
|
||||
@@ -0,0 +1,73 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""把 xiangmu 窗体里的 CJZZ.Controls 第三方控件替换为标准 WinForms 控件,
|
||||
让 SharpDevelop 设计器能正常打开编辑。"""
|
||||
import io
|
||||
import re
|
||||
import os
|
||||
|
||||
BASE = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\xiangmu"
|
||||
|
||||
# 控件类型映射
|
||||
TYPE_MAP = {
|
||||
"CJZZ.Controls.ResizableComboBox": "System.Windows.Forms.ComboBox",
|
||||
"CJZZ.Controls.ResizableTextBox": "System.Windows.Forms.TextBox",
|
||||
"CJZZ.Controls.RoundedButton": "System.Windows.Forms.Button",
|
||||
"CJZZ.Controls.MultiSelectComboBox": "System.Windows.Forms.ComboBox",
|
||||
}
|
||||
|
||||
# 需要删除的 CJZZ 特有属性行(按前缀匹配)
|
||||
CJZZ_PROPS = [
|
||||
"BorderColor", "HoverBorderColor", "FocusBorderColor", "PlaceholderColor",
|
||||
"PlaceholderText", "Flat", "Radius", "Items.PerLine", "Watermark",
|
||||
"ShowClearButton", "ArrowColor", "ArrowHoverColor",
|
||||
]
|
||||
|
||||
|
||||
def fix_file(path):
|
||||
s = io.open(path, encoding="utf-8-sig").read()
|
||||
orig = s
|
||||
n_changed = 0
|
||||
|
||||
# 1) 替换类型声明
|
||||
for old_t, new_t in TYPE_MAP.items():
|
||||
old_decl = "private " + old_t + " "
|
||||
new_decl = "private " + new_t + " "
|
||||
if old_decl in s:
|
||||
s = s.replace(old_decl, new_decl)
|
||||
n_changed += s.count(new_decl) - orig.count(new_decl)
|
||||
old_new = "new " + old_t + "()"
|
||||
new_new = "new " + new_t + "()"
|
||||
if old_new in s:
|
||||
s = s.replace(old_new, new_new)
|
||||
n_changed += 1
|
||||
|
||||
# 2) 删除 CJZZ 特有属性行
|
||||
lines = s.split("\n")
|
||||
out = []
|
||||
removed = 0
|
||||
for line in lines:
|
||||
stripped = line.strip()
|
||||
skip = False
|
||||
for prop in CJZZ_PROPS:
|
||||
# 形如 this.xxx.PropName = ...;
|
||||
m = re.match(r"this\.\w+\.(" + prop + r")\s*=", stripped)
|
||||
if m:
|
||||
skip = True
|
||||
removed += 1
|
||||
break
|
||||
if not skip:
|
||||
out.append(line)
|
||||
s = "\n".join(out)
|
||||
|
||||
if s != orig:
|
||||
io.open(path, "w", encoding="utf-8-sig", newline="\r\n").write(s)
|
||||
print("%s: 类型替换=%d, 删除属性行=%d" % (os.path.basename(path), n_changed, removed))
|
||||
else:
|
||||
print("%s: 无变化" % os.path.basename(path))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for name in os.listdir(BASE):
|
||||
if name.endswith(".ui.cs"):
|
||||
fix_file(os.path.join(BASE, name))
|
||||
print("\n完成。")
|
||||
@@ -0,0 +1,57 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""恢复 pdfbt Form3 四表列定义:照 cadbt Form3.ui.cs 的索引式列(Columns.Add+Columns[N]属性)
|
||||
替换 pdfbt 的空壳字段+AddRange,并清理 57 个无用列字段。幂等(重复跑前检查)。"""
|
||||
import io
|
||||
import re
|
||||
|
||||
CADBT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\cadbt\cadbt\Plugin\Form3.ui.cs"
|
||||
PDFBT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\pdfbt\Plugin\Form3.Designer.cs"
|
||||
|
||||
cs = io.open(CADBT, encoding="utf-8-sig").read()
|
||||
ps = io.open(PDFBT, encoding="utf-8-sig").read()
|
||||
|
||||
TABLES = ["advancedDataGridView6", "advancedDataGridView9", "advancedDataGridView4", "advancedDataGridView5"]
|
||||
|
||||
def 提取cadbt段(tbl):
|
||||
"""从 cadbt 提取该表的 Columns.Add...Columns[N] 连续段(含首尾)。"""
|
||||
i = cs.find("this." + tbl + ".Columns.Add(")
|
||||
assert i > 0, tbl + " Add 未找到"
|
||||
lines = cs[i:].split("\n")
|
||||
out = []
|
||||
for l in lines:
|
||||
t = l.strip()
|
||||
if ("Columns.Add(" in t) or re.match(re.escape(tbl) + r"\.Columns\[\d+\]\.", t):
|
||||
out.append(l)
|
||||
elif out and t == "":
|
||||
# 允许段内不换段(索引段中间无空行),遇其他语句即止
|
||||
continue
|
||||
elif out:
|
||||
break
|
||||
assert out, tbl + " 段为空"
|
||||
return "\n".join(out)
|
||||
|
||||
def 替换pdfbt段(tbl, new_seg):
|
||||
"""把 pdfbt 该表的 Columns.AddRange(...) 块替换为 cadbt 索引段。"""
|
||||
global ps
|
||||
m = re.search(
|
||||
r"this\." + tbl + r"\.Columns\.AddRange\(new System\.Windows\.Forms\.DataGridViewColumn\[\] \{.*?\}\);",
|
||||
ps, re.S)
|
||||
if not m:
|
||||
# 已经替换过(幂等)
|
||||
if "this." + tbl + ".Columns.Add(" in ps:
|
||||
return False
|
||||
raise AssertionError(tbl + " AddRange 未找到")
|
||||
ps = ps[:m.start()] + new_seg + ps[m.end():]
|
||||
return True
|
||||
|
||||
for tbl in TABLES:
|
||||
seg = 提取cadbt段(tbl)
|
||||
n = len([l for l in seg.split("\n") if "Columns.Add(" in l])
|
||||
changed = 替换pdfbt段(tbl, seg)
|
||||
print(tbl, "列数", n, "替换" if changed else "跳过(已改)")
|
||||
|
||||
# 删 57 个字段声明 + new 行(带 \t 或空格缩进两种都匹配)
|
||||
ps = re.sub(r"\n\s*private System\.Windows\.Forms\.DataGridViewTextBoxColumn dataGridViewTextBoxColumn\d+;", "", ps)
|
||||
ps = re.sub(r"\n\s*this\.dataGridViewTextBoxColumn\d+ = new System\.Windows\.Forms\.DataGridViewTextBoxColumn\(\);", "", ps)
|
||||
io.open(PDFBT, "w", encoding="utf-8-sig").write(ps)
|
||||
print("字段清理完成; HeaderText 数:", len(re.findall(r"HeaderText", ps)))
|
||||
@@ -0,0 +1,37 @@
|
||||
// resx2res: 把 .resx(XML) 编译成 .resources(二进制),等价 SDK 的 resgen。
|
||||
// 背景:本机无 Windows SDK 无 resgen.exe;csc 的 /resource 只会原样嵌入文件,
|
||||
// 直接嵌 resx 运行时报"流不是有效的资源文件"。msbuild(SD F5)会自动转换,
|
||||
// 命令行 csc 编译前需先用本工具生成 .resources。
|
||||
// 用法: resx2res <输入.resx> <输出.resources>
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Resources;
|
||||
|
||||
class Resx2Res
|
||||
{
|
||||
static int Main(string[] args)
|
||||
{
|
||||
if (args.Length < 2)
|
||||
{
|
||||
Console.WriteLine("用法: resx2res <in.resx> <out.resources>");
|
||||
return 1;
|
||||
}
|
||||
using (var reader = new ResXResourceReader(args[0]))
|
||||
{
|
||||
reader.UseResXDataNodes = true;
|
||||
using (var writer = new ResourceWriter(args[1]))
|
||||
{
|
||||
foreach (DictionaryEntry e in reader)
|
||||
{
|
||||
var node = e.Value as ResXDataNode;
|
||||
object val = node != null
|
||||
? node.GetValue((System.ComponentModel.Design.ITypeResolutionService)null)
|
||||
: e.Value;
|
||||
writer.AddResource((string)e.Key, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
Console.WriteLine("OK: " + args[1]);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""方案窗口编辑区替换为主窗口完整保存链(虚拟模式版)。heredoc 太长,用文件执行。"""
|
||||
import io
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\DockWindows\DockWindow8\DockWindow8.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
|
||||
start = s.find("\t\t// ============ 方案三表编辑直写")
|
||||
end_anchor = "\t\tprivate string 查方案表2(System.Data.SQLite.SQLiteConnection conn, string 类型, string 图号, string 箱号)"
|
||||
end = s.find(end_anchor)
|
||||
assert start > 0 and end > start, "编辑区定位失败"
|
||||
|
||||
NEW = '''\t\t// ============================================================
|
||||
\t\t// 方案三表实时保存 —— 主窗口报价页保存链的完整复制
|
||||
\t\t// (虚拟模式:CellValueNeeded 供数 / CellValuePushed 写回 /
|
||||
\t\t// 内存重算链乐观显示 / 300ms 防抖队列 / 后台单事务写库+聚合)
|
||||
\t\t// ============================================================
|
||||
|
||||
\t\t/// <summary>虚拟模式供数(主窗口 Dgv_CellValueNeeded 同款)。</summary>
|
||||
\t\tprivate void 方案表_CellValueNeeded(object sender, DataGridViewCellValueEventArgs e)
|
||||
\t\t{
|
||||
\t\t\tvar dgv = sender as DataGridView;
|
||||
\t\t\tvar table = 取方案数据表(dgv);
|
||||
\t\t\tif (table == null || e.RowIndex < 0 || e.RowIndex >= table.Rows.Count) { e.Value = ""; return; }
|
||||
\t\t\tif (e.ColumnIndex < 0 || e.ColumnIndex >= dgv.Columns.Count) { e.Value = ""; return; }
|
||||
\t\t\tstring colName = dgv.Columns[e.ColumnIndex].DataPropertyName;
|
||||
\t\t\tif (string.IsNullOrEmpty(colName) || !table.Columns.Contains(colName)) { e.Value = ""; return; }
|
||||
\t\t\tobject v = table.Rows[e.RowIndex][colName];
|
||||
\t\t\te.Value = (v == null || v == DBNull.Value) ? "" : v;
|
||||
\t\t}
|
||||
|
||||
\t\t/// <summary>编辑立即提交(主窗口 Grid_DirtyStateChanged 同款)。</summary>
|
||||
\t\tprivate void 方案表_DirtyStateChanged(object sender, EventArgs e)
|
||||
\t\t{
|
||||
\t\t\tvar dgv = sender as DataGridView;
|
||||
\t\t\tif (dgv != null && dgv.IsCurrentCellDirty)
|
||||
\t\t\t\tdgv.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
\t\t}
|
||||
|
||||
\t\t/// <summary>虚拟模式写回:写内存表 + 内存重算链(乐观显示) + 入队直写方案文件。</summary>
|
||||
\t\tprivate void 方案表_CellValuePushed(object sender, DataGridViewCellValueEventArgs e)
|
||||
\t\t{
|
||||
\t\t\tvar dgv = sender as DataGridView;
|
||||
\t\t\tvar table = 取方案数据表(dgv);
|
||||
\t\t\tif (table == null || e.RowIndex < 0 || e.RowIndex >= table.Rows.Count) return;
|
||||
\t\t\tif (e.ColumnIndex < 0 || e.ColumnIndex >= dgv.Columns.Count) return;
|
||||
\t\t\tstring colName = dgv.Columns[e.ColumnIndex].DataPropertyName;
|
||||
\t\t\tif (string.IsNullOrEmpty(colName) || colName == "_rowid_" || !table.Columns.Contains(colName)) return;
|
||||
|
||||
\t\t\ttable.Rows[e.RowIndex][colName] = (e.Value == null || e.Value == DBNull.Value) ? DBNull.Value : e.Value;
|
||||
|
||||
\t\t\t// ★ 内存重算链(乐观显示):元件→箱号→图号
|
||||
\t\t\tif (_联动层 == 0 && !_方案派生列.Contains(colName))
|
||||
\t\t\t{
|
||||
\t\t\t\t_联动层++;
|
||||
\t\t\t\ttry
|
||||
\t\t\t\t{
|
||||
\t\t\t\t\tif (dgv == advancedDataGridView元件) 方案重算元件行(e.RowIndex);
|
||||
\t\t\t\t\telse if (dgv == advancedDataGridView箱号) 方案重算箱号行(e.RowIndex);
|
||||
\t\t\t\t\telse if (dgv == advancedDataGridView图号) 方案重算图号行(e.RowIndex);
|
||||
\t\t\t\t}
|
||||
\t\t\t\tfinally { _联动层--; }
|
||||
\t\t\t\tdgv.Invalidate();
|
||||
\t\t\t\tif (dgv == advancedDataGridView元件)
|
||||
\t\t\t\t{
|
||||
\t\t\t\t\tadvancedDataGridView箱号.Invalidate();
|
||||
\t\t\t\t\tadvancedDataGridView图号.Invalidate();
|
||||
\t\t\t\t}
|
||||
\t\t\t\telse if (dgv == advancedDataGridView箱号) advancedDataGridView图号.Invalidate();
|
||||
\t\t\t}
|
||||
|
||||
\t\t\t// ★ 入队直写(主窗口 入队直写库 同款:300ms 防抖聚合)
|
||||
\t\t\tif (!_方案派生列.Contains(colName))
|
||||
\t\t\t\t入队方案直写(dgv, e.RowIndex, colName, e.Value);
|
||||
\t\t}
|
||||
|
||||
\t\t// ---- 内存重算链(主窗口 RecalcYuanjianRow/RecalcXianghaoRowFromYuanjian/RecalcTuhaoRowFromXianghao 的方案版) ----
|
||||
|
||||
\t\tprivate static decimal 方案数值(object v)
|
||||
\t\t{
|
||||
\t\t\tdecimal d;
|
||||
\t\t\treturn v == null || v == DBNull.Value ? 0 :
|
||||
\t\t\t\t(decimal.TryParse(Convert.ToString(v).Trim(), System.Globalization.NumberStyles.Float,
|
||||
\t\t\t\tSystem.Globalization.CultureInfo.InvariantCulture, out d) ? d : 0);
|
||||
\t\t}
|
||||
|
||||
\t\tprivate void 方案重算元件行(int rowIndex)
|
||||
\t\t{
|
||||
\t\t\tvar t = _方案元件表;
|
||||
\t\t\tif (t == null || rowIndex < 0 || rowIndex >= t.Rows.Count) return;
|
||||
\t\t\tvar r = t.Rows[rowIndex];
|
||||
\t\t\tdecimal 表价 = 方案数值(r["表价"]), 采购系数 = 方案数值(r["采购系数"]),
|
||||
\t\t\t\t报出系数 = 方案数值(r["报出系数"]), 数量 = 方案数值(r["数量"]);
|
||||
\t\t\tdecimal 采购单价 = 表价 * 采购系数;
|
||||
\t\t\tif (t.Columns.Contains("采购单价")) r["采购单价"] = 采购单价;
|
||||
\t\t\tif (t.Columns.Contains("采购价")) r["采购价"] = 采购单价 * 数量;
|
||||
\t\t\tif (t.Columns.Contains("报出单价")) r["报出单价"] = 采购单价 * 报出系数;
|
||||
\t\t\tif (t.Columns.Contains("报出价")) r["报出价"] = 采购单价 * 报出系数 * 数量;
|
||||
|
||||
\t\t\t方案重算箱号行全部(); // 所属箱号聚合 → 图号聚合
|
||||
\t\t}
|
||||
|
||||
\t\tprivate void 方案重算箱号行(int rowIndex)
|
||||
\t\t{
|
||||
\t\t\t// 箱号行数量变:按内存元件表 SUM 重算本行 + 图号聚合
|
||||
\t\t\t方案重算箱号行全部();
|
||||
\t\t}
|
||||
|
||||
\t\tprivate void 方案重算箱号行全部()
|
||||
\t\t{
|
||||
\t\t\tvar xh = _方案箱号表;
|
||||
\t\t\tvar yj = _方案元件表;
|
||||
\t\t\tif (xh == null) return;
|
||||
\t\t\tdecimal 采购合计 = 0, 报出合计 = 0;
|
||||
\t\t\tif (yj != null)
|
||||
\t\t\t{
|
||||
\t\t\t\tforeach (DataRow r in yj.Rows)
|
||||
\t\t\t\t{
|
||||
\t\t\t\t\t采购合计 += 方案数值(r["采购价"]);
|
||||
\t\t\t\t\t报出合计 += 方案数值(r["报出价"]);
|
||||
\t\t\t\t}
|
||||
\t\t\t}
|
||||
\t\t\t// 写到当前选中箱号行(方案库每箱号一张元件表,当前元件表即当前箱号)
|
||||
\t\t\tif (advancedDataGridView箱号.CurrentRow != null)
|
||||
\t\t\t{
|
||||
\t\t\t\tint idx = advancedDataGridView箱号.CurrentRow.Index;
|
||||
\t\t\t\tif (idx >= 0 && idx < xh.Rows.Count)
|
||||
\t\t\t\t{
|
||||
\t\t\t\t\tvar row = xh.Rows[idx];
|
||||
\t\t\t\t\tdecimal 数量 = 方案数值(row["数量"]);
|
||||
\t\t\t\t\tif (xh.Columns.Contains("单台成本")) row["单台成本"] = 采购合计;
|
||||
\t\t\t\t\tif (xh.Columns.Contains("成本价")) row["成本价"] = 采购合计 * 数量;
|
||||
\t\t\t\t\tif (xh.Columns.Contains("单台报出")) row["单台报出"] = 报出合计;
|
||||
\t\t\t\t\tif (xh.Columns.Contains("报出价")) row["报出价"] = 报出合计 * 数量;
|
||||
\t\t\t\t\tdecimal 成本价 = 采购合计 * 数量, 报出价 = 报出合计 * 数量;
|
||||
\t\t\t\t\tif (xh.Columns.Contains("利润")) row["利润"] = 报出价 - 成本价;
|
||||
\t\t\t\t\tif (xh.Columns.Contains("利润率")) row["利润率"] = 成本价 == 0 ? 0 : (报出价 - 成本价) / 成本价;
|
||||
\t\t\t\t\tif (xh.Columns.Contains("毛利率")) row["毛利率"] = 报出价 == 0 ? 0 : (报出价 - 成本价) / 报出价;
|
||||
\t\t\t\t}
|
||||
\t\t\t}
|
||||
\t\t\t方案重算图号行全部();
|
||||
\t\t}
|
||||
|
||||
\t\tprivate void 方案重算图号行(int rowIndex)
|
||||
\t\t{
|
||||
\t\t\t方案重算图号行全部();
|
||||
\t\t}
|
||||
|
||||
\t\tprivate void 方案重算图号行全部()
|
||||
\t\t{
|
||||
\t\t\tvar th = _方案图号表;
|
||||
\t\t\tvar xh = _方案箱号表;
|
||||
\t\t\tif (th == null || xh == null) return;
|
||||
\t\t\tdecimal 箱柜数量 = 0, 报出合计 = 0, 成本合计 = 0;
|
||||
\t\t\tforeach (DataRow r in xh.Rows)
|
||||
\t\t\t{
|
||||
\t\t\t\t箱柜数量 += 方案数值(r["数量"]);
|
||||
\t\t\t\t报出合计 += 方案数值(r["报出价"]);
|
||||
\t\t\t\t成本合计 += 方案数值(r["成本价"]);
|
||||
\t\t\t}
|
||||
\t\t\tif (advancedDataGridView图号.CurrentRow != null)
|
||||
\t\t\t{
|
||||
\t\t\t\tint idx = advancedDataGridView图号.CurrentRow.Index;
|
||||
\t\t\t\tif (idx >= 0 && idx < th.Rows.Count)
|
||||
\t\t\t\t{
|
||||
\t\t\t\t\tvar row = th.Rows[idx];
|
||||
\t\t\t\t\tdecimal 台数 = 方案数值(row["台数"]);
|
||||
\t\t\t\t\tdecimal 总价 = 报出合计 * 台数, 成本总价 = 成本合计 * 台数;
|
||||
\t\t\t\t\tif (th.Columns.Contains("箱柜数量")) row["箱柜数量"] = 箱柜数量;
|
||||
\t\t\t\t\tif (th.Columns.Contains("单价")) row["单价"] = 报出合计;
|
||||
\t\t\t\t\tif (th.Columns.Contains("总价")) row["总价"] = 总价;
|
||||
\t\t\t\t\tif (th.Columns.Contains("成本总价")) row["成本总价"] = 成本总价;
|
||||
\t\t\t\t\tif (th.Columns.Contains("毛利")) row["毛利"] = 总价 - 成本总价;
|
||||
\t\t\t\t\tif (th.Columns.Contains("毛利率")) row["毛利率"] = 总价 == 0 ? 0 : (总价 - 成本总价) / 总价;
|
||||
\t\t\t\t}
|
||||
\t\t\t}
|
||||
\t\t}
|
||||
|
||||
\t\t// ---- 防抖队列 + 后台事务(主窗口 编辑直写库.cs 的方案版) ----
|
||||
|
||||
\t\t/// <summary>派生列集合(由计算维护;用户编辑这些列不触发直写)。</summary>
|
||||
\t\tprivate static readonly System.Collections.Generic.HashSet<string> _方案派生列 =
|
||||
\t\t\tnew System.Collections.Generic.HashSet<string>(
|
||||
\t\t\t\tnew[] { "采购单价", "采购价", "报出单价", "报出价", "单台成本", "成本价", "单台报出",
|
||||
\t\t\t\t\t"利润", "利润率", "毛利率", "箱柜数量", "单价", "总价", "成本总价", "毛利" });
|
||||
|
||||
\t\t/// <summary>待写库项:表名|rowid|列 → 值(后写覆盖先写)。主窗口 _待写项 同款。</summary>
|
||||
\t\tprivate readonly System.Collections.Generic.Dictionary<string, string[]> _方案待写项 =
|
||||
\t\t\tnew System.Collections.Generic.Dictionary<string, string[]>();
|
||||
|
||||
\t\t/// <summary>防抖定时器(300ms)。主窗口 _直写Timer 同款。</summary>
|
||||
\t\tprivate Timer _方案直写Timer;
|
||||
|
||||
\t\tprivate void 入队方案直写(DataGridView dgv, int rowIndex, string colName, object value)
|
||||
\t\t{
|
||||
\t\t\tvar table = 取方案数据表(dgv);
|
||||
\t\t\tif (table == null || !table.Columns.Contains("_rowid_")) return;
|
||||
\t\t\tif (rowIndex < 0 || rowIndex >= table.Rows.Count) return;
|
||||
\t\t\tlong rowid = table.Rows[rowIndex]["_rowid_"] is long ? Convert.ToInt64(table.Rows[rowIndex]["_rowid_"]) : 0;
|
||||
\t\t\tif (rowid <= 0) return;
|
||||
|
||||
\t\t\tlock (_方案待写项)
|
||||
\t\t\t{
|
||||
\t\t\t\t_方案待写项[table.TableName + "|" + rowid + "|" + colName] =
|
||||
\t\t\t\t\tnew[] { table.TableName, rowid.ToString(), colName, value == null ? "" : value.ToString() };
|
||||
\t\t\t}
|
||||
\t\t\tif (_方案直写Timer == null)
|
||||
\t\t\t{
|
||||
\t\t\t\t_方案直写Timer = new Timer();
|
||||
\t\t\t\t_方案直写Timer.Interval = 300;
|
||||
\t\t\t\t_方案直写Timer.Tick += 方案直写到期;
|
||||
\t\t\t}
|
||||
\t\t\t_方案直写Timer.Stop();
|
||||
\t\t\t_方案直写Timer.Start();
|
||||
\t\t}
|
||||
|
||||
\t\t/// <summary>防抖到期:批量直写方案文件(后台单事务)+按公式刷聚合。</summary>
|
||||
\t\tprivate void 方案直写到期(object sender, EventArgs e)
|
||||
\t\t{
|
||||
\t\t\t_方案直写Timer.Stop();
|
||||
\t\t\tif (_方案待写项.Count == 0 || string.IsNullOrEmpty(_当前方案文件)) return;
|
||||
|
||||
\t\t\tSystem.Collections.Generic.List<string[]> 批;
|
||||
\t\t\tlock (_方案待写项)
|
||||
\t\t\t{
|
||||
\t\t\t\t批 = new System.Collections.Generic.List<string[]>(_方案待写项.Values);
|
||||
\t\t\t\t_方案待写项.Clear();
|
||||
\t\t\t}
|
||||
|
||||
\t\t\tstring dbPath = _当前方案文件;
|
||||
\t\t\tSystem.Threading.Tasks.Task.Run(delegate
|
||||
\t\t\t{
|
||||
\t\t\t\ttry
|
||||
\t\t\t\t{
|
||||
\t\t\t\t\tusing (var conn = new System.Data.SQLite.SQLiteConnection(连接串(dbPath)))
|
||||
\t\t\t\t\t{
|
||||
\t\t\t\t\t\tconn.Open();
|
||||
\t\t\t\t\t\tusing (var tx = conn.BeginTransaction())
|
||||
\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\tforeach (var item in 批)
|
||||
\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\tusing (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
\t\t\t\t\t\t\t\t\t"UPDATE \\"" + item[0].Replace("\\"", "") + "\\" SET \\"" + item[2].Replace("\\"", "") +
|
||||
\t\t\t\t\t\t\t\t\t"\\"=@v WHERE rowid=@r;", conn, tx))
|
||||
\t\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\t\tcmd.Parameters.AddWithValue("@v", item[3]);
|
||||
\t\t\t\t\t\t\t\t\tcmd.Parameters.AddWithValue("@r", long.Parse(item[1]));
|
||||
\t\t\t\t\t\t\t\t\tcmd.ExecuteNonQuery();
|
||||
\t\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\t\t// 元件行:重算本行派生价
|
||||
\t\t\t\t\t\t\t\tif (item[0].StartsWith("yj_"))
|
||||
\t\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\t\tstring yjSet = 计算引擎.生成Set片段(计算引擎.加载公式(GetFak连接(), "元件"), "ROW", null);
|
||||
\t\t\t\t\t\t\t\t\tif (yjSet.Length > 0)
|
||||
\t\t\t\t\t\t\t\t\t\tExec(conn, tx, "UPDATE \\"" + item[0] + "\\" SET " + yjSet + " WHERE rowid=" + item[1] + ";");
|
||||
\t\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\t}
|
||||
|
||||
\t\t\t\t\t\t\t// 聚合链:反查 图号/箱号 → 箱号行聚合 + 图号行聚合
|
||||
\t\t\t\t\t\t\tstring 图号 = null, 箱号 = null;
|
||||
\t\t\t\t\t\t\tforeach (var item in 批)
|
||||
\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\tif (item[0].StartsWith("yj_"))
|
||||
\t\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\t\tusing (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
\t\t\t\t\t\t\t\t\t\t"SELECT 图号,箱号 FROM 表索引 WHERE 类型='元件' AND 实际表名=@t LIMIT 1;", conn, tx))
|
||||
\t\t\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\t\t\tcmd.Parameters.AddWithValue("@t", item[0]);
|
||||
\t\t\t\t\t\t\t\t\t\tusing (var r = cmd.ExecuteReader())
|
||||
\t\t\t\t\t\t\t\t\t\t\tif (r.Read()) { 图号 = r.IsDBNull(0) ? null : r.GetString(0); 箱号 = r.IsDBNull(1) ? null : r.GetString(1); }
|
||||
\t\t\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\t\telse if (item[0].StartsWith("xh_"))
|
||||
\t\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\t\tusing (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
\t\t\t\t\t\t\t\t\t\t"SELECT 图号 FROM 表索引 WHERE 类型='箱号' AND 实际表名=@t LIMIT 1;", conn, tx))
|
||||
\t\t\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\t\t\tcmd.Parameters.AddWithValue("@t", item[0]);
|
||||
\t\t\t\t\t\t\t\t\t\tusing (var r = cmd.ExecuteReader())
|
||||
\t\t\t\t\t\t\t\t\t\t\tif (r.Read()) { 图号 = r.IsDBNull(0) ? null : r.GetString(0); 箱号 = null; }
|
||||
\t\t\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\tif (!string.IsNullOrEmpty(图号))
|
||||
\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\tstring xhTbl = 查方案表2(conn, tx, "箱号", 图号, null);
|
||||
\t\t\t\t\t\t\t\tif (!string.IsNullOrEmpty(xhTbl))
|
||||
\t\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\t\tif (!string.IsNullOrEmpty(箱号))
|
||||
\t\t\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\t\t\tstring yjTbl = 查方案表2(conn, tx, "元件", 图号, 箱号);
|
||||
\t\t\t\t\t\t\t\t\t\tif (!string.IsNullOrEmpty(yjTbl))
|
||||
\t\t\t\t\t\t\t\t\t\t{
|
||||
\t\t\t\t\t\t\t\t\t\t\tstring xhSet = 计算引擎.生成Set片段(计算引擎.加载公式(GetFak连接(), "箱号"), "ROW", yjTbl);
|
||||
\t\t\t\t\t\t\t\t\t\t\t\tif (xhSet.Length > 0)
|
||||
\t\t\t\t\t\t\t\t\t\t\t\t\tExec(conn, tx, "UPDATE \\"" + xhTbl + "\\" SET " + xhSet + " WHERE \\"内部ID\\"='" + 箱号.Replace("'", "''") + "';");
|
||||
\t\t\t\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\t\t\tstring thSet = 计算引擎.生成Set片段(计算引擎.加载公式(GetFak连接(), "图号"), "ROW", xhTbl);
|
||||
\t\t\t\t\t\t\t\t\tif (thSet.Length > 0)
|
||||
\t\t\t\t\t\t\t\t\t\tExec(conn, tx, "UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号.Replace("'", "''") + "';");
|
||||
\t\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\t}
|
||||
\t\t\t\t\t\t\ttx.Commit();
|
||||
\t\t\t\t\t\t}
|
||||
\t\t\t\t\t}
|
||||
\t\t\t\t\t写联动日志("方案直写完成: " + 批.Count + " 项");
|
||||
\t\t\t\t}
|
||||
\t\t\t\tcatch (Exception ex)
|
||||
\t\t\t\t{
|
||||
\t\t\t\t\t写联动日志("方案直写异常: " + ex.Message);
|
||||
\t\t\t\t}
|
||||
\t\t\t});
|
||||
\t\t}
|
||||
|
||||
\t\t/// <summary>简单 SQL 执行。</summary>
|
||||
\t\tprivate static void Exec(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx, string sql)
|
||||
\t\t{
|
||||
\t\t\tusing (var cmd = new System.Data.SQLite.SQLiteCommand(sql, conn, tx))
|
||||
\t\t\t\tcmd.ExecuteNonQuery();
|
||||
\t\t}
|
||||
|
||||
\t\t/// <summary>方案窗口自己的 fak.db 连接(公式表来源;已打开)。</summary>
|
||||
\t\tprivate System.Data.SQLite.SQLiteConnection GetFak连接()
|
||||
\t\t{
|
||||
\t\t\tvar conn = new System.Data.SQLite.SQLiteConnection("Data Source=" +
|
||||
\t\t\t\tPath.Combine(AppDomain.CurrentDomain.BaseDirectory, "sjk", "fak.db") + ";Version=3;");
|
||||
\t\t\tconn.Open();
|
||||
\t\t\treturn conn;
|
||||
\t\t}
|
||||
|
||||
'''
|
||||
s = s[:start] + NEW + s[end:]
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("编辑直写区已替换为主窗口完整链路")
|
||||
@@ -0,0 +1,215 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""甲方报表 FillDetailSheet 重写:DOM 组装(上万行节点树+OuterXml 双份字符串→32位OOM)
|
||||
改为字符串模板替换法:提取模板行字符串→逐行换号/替换单元格→拼接输出。零 DOM、单份字符串。"""
|
||||
import io
|
||||
import re
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\xiangmu\甲方报表.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
|
||||
start_anchor = " private void FillDetailSheet(Dictionary<string, byte[]> entries, string sheetKey,"
|
||||
end_anchor = " entries[sheetKey] = System.Text.Encoding.UTF8.GetBytes(doc.OuterXml);\n }"
|
||||
|
||||
i0 = s.find(start_anchor)
|
||||
assert i0 > 0, "起点未找到"
|
||||
i1 = s.find(end_anchor, i0)
|
||||
assert i1 > 0, "终点未找到"
|
||||
i1 += len(end_anchor)
|
||||
|
||||
NEW = ''' // ============ 明细表:字符串模板替换法(零 DOM——上万像素级 DOM+OuterXml 双份字符串会 OOM) ============
|
||||
|
||||
/// <summary>行字符串:更新行号与全部单元格引用(r="列行号")。</summary>
|
||||
private static string 行换号(string rowXml, int rowNum)
|
||||
{
|
||||
rowXml = System.Text.RegularExpressions.Regex.Replace(rowXml, "<row r=\\"\\\\d+\\"", "<row r=\\"" + rowNum + "\\"");
|
||||
return System.Text.RegularExpressions.Regex.Replace(rowXml, "r=\\"([A-Z]+)\\\\d+\\"", "r=\\"$1" + rowNum + "\\"");
|
||||
}
|
||||
|
||||
/// <summary>行字符串:设置某列单元格内容(innerXml=单元格内部 XML;tAttr=类型属性,null=移除 t)。</summary>
|
||||
private static string 行设单元格(string rowXml, string col, int rowNum, string innerXml, string tAttr)
|
||||
{
|
||||
string r = col + rowNum;
|
||||
var m = System.Text.RegularExpressions.Regex.Match(rowXml, "<c r=\\"" + r + "\\"([^>]*?)(?:/>|>.*?</c>)");
|
||||
if (!m.Success) return rowXml;
|
||||
string attrs = m.Groups[1].Value;
|
||||
attrs = System.Text.RegularExpressions.Regex.Replace(attrs, "\\\\s*t=\\"[^\\"]*\\"", "");
|
||||
if (!string.IsNullOrEmpty(tAttr)) attrs += " t=\\"" + tAttr + "\\"";
|
||||
return rowXml.Substring(0, m.Index) + "<c r=\\"" + r + "\\"" + attrs + ">" + innerXml + "</c>" + rowXml.Substring(m.Index + m.Length);
|
||||
}
|
||||
|
||||
private static string 行设文本(string rowXml, string col, int rowNum, string text)
|
||||
{ return 行设单元格(rowXml, col, rowNum, "<v>" + XmlEscape(text) + "</v>", "str"); }
|
||||
|
||||
private static string 行设数值(string rowXml, string col, int rowNum, decimal v)
|
||||
{ return 行设单元格(rowXml, col, rowNum, "<v>" + v.ToString(System.Globalization.CultureInfo.InvariantCulture) + "</v>", null); }
|
||||
|
||||
private static string 行设公式(string rowXml, string col, int rowNum, string formula)
|
||||
{ return 行设单元格(rowXml, col, rowNum, "<f>" + XmlEscape(formula) + "</f>", null); }
|
||||
|
||||
private void FillDetailSheet(Dictionary<string, byte[]> entries, string sheetKey,
|
||||
List<TuhaoData> tuhaoList, List<CabinetRowInfo> cabRowInfos, System.Text.StringBuilder dbgLog,
|
||||
bool useFormula)
|
||||
{
|
||||
string xml = System.Text.Encoding.UTF8.GetString(entries[sheetKey]);
|
||||
|
||||
// 提取全部行块,定位模板行 11~16 与头/尾(零 DOM)
|
||||
var 行匹配 = System.Text.RegularExpressions.Regex.Matches(
|
||||
xml, "<row r=\\"(\\\\d+)\\"[^>]*?(?:/>|>.*?</row>)");
|
||||
if (行匹配.Count == 0) return;
|
||||
|
||||
string[] tpl = new string[6];
|
||||
int bodyStart = -1, bodyEnd = -1;
|
||||
foreach (System.Text.RegularExpressions.Match m in 行匹配)
|
||||
{
|
||||
int rn = int.Parse(m.Groups[1].Value);
|
||||
if (rn >= 11 && rn <= 16)
|
||||
{
|
||||
tpl[rn - 11] = m.Value;
|
||||
if (bodyStart < 0) bodyStart = m.Index;
|
||||
bodyEnd = m.Index + m.Length;
|
||||
}
|
||||
}
|
||||
string tplPaperRow = tpl[0], tplCabRow = tpl[1], tplHeaderRow = tpl[2],
|
||||
tplElemRow = tpl[3], tplSingleRow = tpl[4], tplGrandRow = tpl[5];
|
||||
if (tplCabRow == null || tplElemRow == null) return;
|
||||
|
||||
string 头 = xml.Substring(0, bodyStart); // 行1~10
|
||||
string 尾 = xml.Substring(bodyEnd); // 行17(签名)及之后
|
||||
|
||||
var sb = new System.Text.StringBuilder();
|
||||
int rowNum = 11;
|
||||
decimal grandQty = 0, grandTotal = 0;
|
||||
int tuhaoIdx = 0;
|
||||
|
||||
foreach (var tuhao in tuhaoList)
|
||||
{
|
||||
string tuhaoName = !string.IsNullOrEmpty(tuhao.TuMing) ? tuhao.TuMing : tuhao.Tuhao;
|
||||
|
||||
// 图名行
|
||||
if (tplPaperRow != null)
|
||||
{
|
||||
string row = 行换号(tplPaperRow, rowNum);
|
||||
row = 行设文本(row, "A", rowNum, tuhaoName);
|
||||
sb.Append(row);
|
||||
rowNum++;
|
||||
}
|
||||
|
||||
// 每个箱号: 柜号行 + 表头 + 元件行*N + 单台合计 + 总计
|
||||
int cabOrder = 0;
|
||||
foreach (var cab in tuhao.Cabinets)
|
||||
{
|
||||
cabOrder++;
|
||||
int cabRowNum = rowNum;
|
||||
|
||||
// 柜号行
|
||||
{
|
||||
string row = 行换号(tplCabRow, rowNum);
|
||||
row = 行设文本(row, "A", rowNum, (tuhaoIdx + 1) + "-" + cabOrder);
|
||||
row = 行设文本(row, "B", rowNum, "柜号:" + cab.CabinetNo);
|
||||
row = 行设文本(row, "D", rowNum, cab.Model);
|
||||
// ★ G列"名称:"= 箱名(进线柜/PT柜等);库里没有时退回图名(与旧版行为一致)
|
||||
string 名称值 = !string.IsNullOrEmpty(cab.BoxName) ? cab.BoxName : tuhaoName;
|
||||
row = 行设文本(row, "G", rowNum, 名称值);
|
||||
row = 行设文本(row, "I", rowNum, cab.SpecName);
|
||||
sb.Append(row);
|
||||
rowNum++;
|
||||
}
|
||||
|
||||
// 表头行
|
||||
if (tplHeaderRow != null)
|
||||
{
|
||||
sb.Append(行换号(tplHeaderRow, rowNum));
|
||||
rowNum++;
|
||||
}
|
||||
|
||||
// 元件行(G列用 =E*F 公式)
|
||||
int elemStartRow = rowNum;
|
||||
if (cab.Elements.Count == 0)
|
||||
{
|
||||
sb.Append(行换号(tplElemRow, rowNum));
|
||||
rowNum++;
|
||||
}
|
||||
else
|
||||
{
|
||||
int elemOrder = 0;
|
||||
foreach (var elem in cab.Elements)
|
||||
{
|
||||
elemOrder++;
|
||||
string row = 行换号(tplElemRow, rowNum);
|
||||
row = 行设文本(row, "A", rowNum, elemOrder.ToString());
|
||||
row = 行设文本(row, "B", rowNum, elem.Name);
|
||||
row = 行设文本(row, "C", rowNum, elem.ShowSpecName);
|
||||
row = 行设文本(row, "D", rowNum, elem.Unit);
|
||||
row = 行设数值(row, "E", rowNum, SafeDec(elem.Count));
|
||||
row = 行设数值(row, "F", rowNum, elem.OfferUnitPrice);
|
||||
// G列: 公式 =E{行}*F{行} 或直接值
|
||||
if (useFormula)
|
||||
row = 行设公式(row, "G", rowNum, "E" + rowNum + "*F" + rowNum);
|
||||
else
|
||||
row = 行设数值(row, "G", rowNum, SafeDec(elem.Count) * elem.OfferUnitPrice);
|
||||
row = 行设文本(row, "H", rowNum, elem.FactName);
|
||||
row = 行设文本(row, "I", rowNum, elem.Remark);
|
||||
sb.Append(row);
|
||||
rowNum++;
|
||||
}
|
||||
}
|
||||
int elemEndRow = rowNum - 1;
|
||||
|
||||
// 单台合计行(G列用 =ROUND(SUM(G{起}:G{止}),0) 公式)
|
||||
int singleRowNum = rowNum;
|
||||
if (tplSingleRow != null)
|
||||
{
|
||||
string row = 行换号(tplSingleRow, rowNum);
|
||||
row = 行设文本(row, "B", rowNum, "单台合计");
|
||||
if (useFormula)
|
||||
row = 行设公式(row, "G", rowNum, "ROUND(SUM(G" + elemStartRow + ":G" + elemEndRow + "),0)");
|
||||
else
|
||||
{
|
||||
decimal elemSum = 0;
|
||||
foreach (var elem in cab.Elements) elemSum += SafeDec(elem.Count) * elem.OfferUnitPrice;
|
||||
row = 行设数值(row, "G", rowNum, elemSum);
|
||||
}
|
||||
sb.Append(row);
|
||||
rowNum++;
|
||||
}
|
||||
|
||||
// 总计行(G列用 =G{单台合计}*E{总计} 公式)
|
||||
int grandRowNum = rowNum;
|
||||
if (tplGrandRow != null)
|
||||
{
|
||||
string row = 行换号(tplGrandRow, rowNum);
|
||||
row = 行设文本(row, "B", rowNum, "总计");
|
||||
row = 行设文本(row, "D", rowNum, "台");
|
||||
row = 行设数值(row, "E", rowNum, SafeDec(cab.Count));
|
||||
if (useFormula)
|
||||
row = 行设公式(row, "G", rowNum, "G" + singleRowNum + "*E" + rowNum);
|
||||
else
|
||||
{
|
||||
decimal elemSum = 0;
|
||||
foreach (var elem in cab.Elements) elemSum += SafeDec(elem.Count) * elem.OfferUnitPrice;
|
||||
row = 行设数值(row, "G", rowNum, elemSum * SafeDec(cab.Count));
|
||||
}
|
||||
sb.Append(row);
|
||||
rowNum++;
|
||||
}
|
||||
|
||||
// 记录行号供汇总表引用
|
||||
cabRowInfos.Add(new CabinetRowInfo
|
||||
{
|
||||
CabRow = cabRowNum,
|
||||
SingleTotalRow = singleRowNum,
|
||||
GrandRow = grandRowNum
|
||||
});
|
||||
|
||||
grandQty += SafeDec(cab.Count);
|
||||
grandTotal += cab.OfferTotalPrice;
|
||||
}
|
||||
tuhaoIdx++;
|
||||
}
|
||||
|
||||
entries[sheetKey] = System.Text.Encoding.UTF8.GetBytes(头 + sb.ToString() + 尾);
|
||||
}'''
|
||||
|
||||
s = s[:i0] + NEW + s[i1:]
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("FillDetailSheet 已重写为字符串模板法")
|
||||
@@ -0,0 +1,86 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""MenuProject_AddToFak_Click 重写为:复制项目库文件为方案(一个方案一个库文件)。幂等(按方法名定位替换)。"""
|
||||
import io
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\主窗口.菜单与库.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
|
||||
si = None
|
||||
for i, l in enumerate(s.split("\n")):
|
||||
if l.startswith(" private void MenuProject_AddToFak_Click"):
|
||||
si = i
|
||||
break
|
||||
assert si is not None, "MenuProject 方法未找到"
|
||||
|
||||
lines = s.split("\n")
|
||||
ei = None
|
||||
for i in range(si + 1, len(lines)):
|
||||
if lines[i] == " }":
|
||||
ei = i
|
||||
break
|
||||
assert ei is not None, "方法结束未找到"
|
||||
|
||||
NEW = r''' private void MenuProject_AddToFak_Click(object sender, EventArgs e)
|
||||
{
|
||||
var row = GetFirstSelectedRow(this.advancedDataGridView6);
|
||||
if (row == null)
|
||||
{
|
||||
MessageBox.Show(this, "请先在项目列表中选择要存为方案的项目。",
|
||||
"存为方案", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
string projectId = GetBoundValueAsString(row, "项目编号");
|
||||
string projName = GetBoundValueAsString(row, "项目名称");
|
||||
if (string.IsNullOrWhiteSpace(projectId))
|
||||
{
|
||||
MessageBox.Show(this, "选中行没有项目编号,无法存为方案。",
|
||||
"存为方案", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
string srcDbPath = GetQuoteDbPath(projectId);
|
||||
if (!System.IO.File.Exists(srcDbPath))
|
||||
{
|
||||
MessageBox.Show(this, "项目报价库不存在:\n" + srcDbPath,
|
||||
"存为方案", MessageBoxButtons.OK, MessageBoxIcon.Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
// 方案名默认 = 项目名称(空则项目编号);重名自动加序号
|
||||
string 方案名 = !string.IsNullOrWhiteSpace(projName) ? projName : projectId;
|
||||
string dstDir = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "sjk", "方案库");
|
||||
System.IO.Directory.CreateDirectory(dstDir);
|
||||
string dstPath = System.IO.Path.Combine(dstDir, 方案名 + ".db");
|
||||
int seq = 1;
|
||||
while (System.IO.File.Exists(dstPath))
|
||||
{
|
||||
seq++;
|
||||
dstPath = System.IO.Path.Combine(dstDir, 方案名 + "(" + seq + ").db");
|
||||
}
|
||||
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
Cursor = Cursors.WaitCursor;
|
||||
try
|
||||
{
|
||||
// ★ 一个方案 = 一份独立项目库副本(含全部图号/箱号/元件/项目信息)
|
||||
BackupSQLiteDb(srcDbPath, dstPath);
|
||||
sw.Stop();
|
||||
Log("[存为方案] 项目=" + projectId + " → " + dstPath + " 耗时 " + sw.ElapsedMilliseconds + "ms");
|
||||
MessageBox.Show(this, "已把项目 [" + projectId + "] 存为方案。\n方案文件: " + dstPath +
|
||||
"\n耗时 " + sw.ElapsedMilliseconds + "ms。", "存为方案",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}
|
||||
catch (Exception exSave)
|
||||
{
|
||||
MessageBox.Show(this, "存为方案失败:\n" + exSave.Message, "存为方案",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
}
|
||||
finally
|
||||
{
|
||||
Cursor = Cursors.Default;
|
||||
}
|
||||
}'''
|
||||
|
||||
lines[si:ei + 1] = NEW.split("\n")
|
||||
io.open(P, "w", encoding="utf-8-sig").write("\n".join(lines))
|
||||
print("MenuProject 重写完成")
|
||||
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""把 24 个按钮的 Image 行(FromFile 形态)写进 MainForm.Designer.cs——设计器里直接显示图标。
|
||||
锚定 .Name = "原字段名" 行(SD 重序列化后变量名是 ribbonButtonN,不能依赖变量名),Image 紧随其后。
|
||||
路径用本机绝对路径(设计器进程 CWD 不可控);FromFile 找不到文件返回 null 不抛异常,
|
||||
换机后设计器只是不画图,运行时由 MainForm.cs 用 BaseDirectory 兜底。幂等。"""
|
||||
import io
|
||||
import os
|
||||
import re
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件"
|
||||
DSG = os.path.join(ROOT, "成套报价软件", "MainForm.Designer.cs")
|
||||
ICON_DIR = os.path.join(ROOT, "成套报价软件", "bin", "Debug", "icons")
|
||||
|
||||
BIND = {
|
||||
"button1": "new-project", "button2": "edit-project", "button3": "delete-project",
|
||||
"button4": "open-project", "button5": "copy-project", "button7": "export-project",
|
||||
"button8": "import-project", "button9": "open-quote", "button10": "edit-quote",
|
||||
"button17": "save", "button18": "import-quote", "button16": "report",
|
||||
"button15": "busbar", "button13": "factor", "button33": "sort-components",
|
||||
"button29": "template-cost", "button11": "cad-grab",
|
||||
"btn登录": "login", "btn修改密码": "password", "btn会员信息": "member",
|
||||
"btn企业信息": "company", "btn保存布局": "save-layout",
|
||||
"btn备份": "backup", "btn恢复": "restore",
|
||||
}
|
||||
|
||||
s = io.open(DSG, encoding="utf-8-sig").read()
|
||||
if ".Image = Smart.CustomComponents.RibbonButton.FromFile(" in s:
|
||||
print("Image 行已存在,跳过")
|
||||
else:
|
||||
lines = s.split("\n")
|
||||
out = []
|
||||
n = 0
|
||||
name_re = re.compile(r'^\t\t\t([A-Za-z0-9_]+)\.Name = "([^"]+)";')
|
||||
for line in lines:
|
||||
out.append(line)
|
||||
m = name_re.match(line)
|
||||
if m:
|
||||
var, btn = m.group(1), m.group(2)
|
||||
if btn in BIND:
|
||||
png = os.path.join(ICON_DIR, BIND[btn] + ".png")
|
||||
out.append('\t\t\t%s.Image = Smart.CustomComponents.RibbonButton.FromFile("%s");'
|
||||
% (var, png.replace("\\", "\\\\")))
|
||||
n += 1
|
||||
assert n == 24, "只锚定 %d 个按钮(期望 24)" % n
|
||||
io.open(DSG, "w", encoding="utf-8-sig").write("\n".join(out))
|
||||
print("Designer.cs 已插入 %d 行 Image(FromFile)" % n)
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""修 Designer.cs 集合块的局部变量重名:三个页签的 rtab1/rgrpA.. 按页加唯一前缀。"""
|
||||
import io
|
||||
import re
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\MainForm.Designer.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
|
||||
start = s.find("\t\t\tSmart.CustomComponents.RibbonTab rtab1 = new")
|
||||
endmark = "\t\t\tthis.ribbonStrip1.TabIndex = 3;"
|
||||
end = s.find(endmark)
|
||||
assert start > 0 and end > start, "找不到集合块"
|
||||
block = s[start:end]
|
||||
|
||||
# 按页签轮次分段:每轮以 new RibbonTab 开头
|
||||
parts = re.split(r"(?=\t\t\tSmart\.CustomComponents\.RibbonTab rtab1 = new)", block)
|
||||
parts = [p for p in parts if p.strip()]
|
||||
assert len(parts) == 3, "期望 3 个页签块,实际 %d" % len(parts)
|
||||
|
||||
prefixes = ["项目_", "报价_", "设置_"]
|
||||
out = []
|
||||
for p, pre in zip(parts, prefixes):
|
||||
p = p.replace("rtab1", "rtab" + pre)
|
||||
# 组变量 rgrpA..D 每轮加页前缀
|
||||
for g in ["A", "B", "C", "D"]:
|
||||
p = p.replace("rgrp" + g, "rgrp" + pre + g)
|
||||
out.append(p)
|
||||
|
||||
s = s[:start] + "".join(out) + s[end:]
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("集合块变量改名完成: rtab项目_/rtab报价_/rtab设置_")
|
||||
@@ -0,0 +1,424 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""把主窗口顶部 tabControl1(项目/报价/设置 三页签×24按钮)替换为 RibbonStrip 功能区(主功能区子类)。
|
||||
改写 MainForm.Designer.cs / MainForm.cs / 主窗口.cs / MainForm.旧页签兼容.cs。
|
||||
幂等:重复运行前先检查关键标记是否已存在。"""
|
||||
import io
|
||||
import re
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
|
||||
|
||||
# 24 个按钮: (原字段名, Text)
|
||||
BTN_PROJECT = [("button1", "新建项目"), ("button2", "修改项目"), ("button3", "删除项目"),
|
||||
("button4", "打开项目"), ("button5", "复制项目")]
|
||||
BTN_PROJECT2 = [("button8", "导入项目"), ("button7", "导出项目")]
|
||||
BTN_QUOTE1 = [("button9", "打开项目"), ("button10", "修改项目"), ("button17", "保存")]
|
||||
BTN_QUOTE2 = [("button18", "导入报价"), ("button16", "甲方报表")]
|
||||
BTN_QUOTE3 = [("button15", "调母排"), ("button13", "调系数"), ("button33", "元件排序"), ("button29", "模板费用")]
|
||||
BTN_QUOTE4 = [("button11", "CAD扒图")]
|
||||
BTN_SET1 = [("btn登录", "登录"), ("btn修改密码", "修改密码"), ("btn会员信息", "会员信息")]
|
||||
BTN_SET2 = [("btn企业信息", "企业信息"), ("btn保存布局", "保存布局")]
|
||||
BTN_SET3 = [("btn备份", "备份"), ("btn恢复", "恢复")]
|
||||
ALL_BTNS = [b for grp in (BTN_PROJECT, BTN_PROJECT2, BTN_QUOTE1, BTN_QUOTE2, BTN_QUOTE3,
|
||||
BTN_QUOTE4, BTN_SET1, BTN_SET2, BTN_SET3) for b in grp]
|
||||
TAB_PAGES = ["tabPage项目", "tabPage报价", "tabPage设置"]
|
||||
BTN_NAMES = set(n for n, _ in ALL_BTNS)
|
||||
|
||||
|
||||
def read(p):
|
||||
with io.open(p, "r", encoding="utf-8-sig") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def write(p, s):
|
||||
with io.open(p, "w", encoding="utf-8-sig") as f:
|
||||
f.write(s)
|
||||
|
||||
|
||||
def must_remove(line):
|
||||
"""Designer.cs 里要删的行:24 按钮 + tabControl1 + 3 tabPage 的字段/new/suspend/resume/属性行。"""
|
||||
s = line.strip()
|
||||
# 按钮字段/new
|
||||
m = re.match(r"private System\.Windows\.Forms\.Button (button\d+|btn[^;]+);$", s)
|
||||
if m and m.group(1).strip() in BTN_NAMES:
|
||||
return True
|
||||
m = re.match(r"this\.(button\d+|btn[^ =]+) = new System\.Windows\.Forms\.Button\(\);$", s)
|
||||
if m and m.group(1) in BTN_NAMES:
|
||||
return True
|
||||
# 按钮属性行(this.buttonN.xxx = ...)
|
||||
m = re.match(r"this\.([A-Za-z0-9_]+)\.[A-Za-z0-9_]+ = ", s)
|
||||
if m and m.group(1) in BTN_NAMES:
|
||||
return True
|
||||
# tabControl1/tabPage
|
||||
if re.match(r"private System\.Windows\.Forms\.TabControl tabControl1;$", s):
|
||||
return True
|
||||
if s == "this.tabControl1 = new System.Windows.Forms.TabControl();":
|
||||
return True
|
||||
if s.startswith("this.tabControl1.") or s == "this.tabControl1.ResumeLayout(false);":
|
||||
return True
|
||||
for tp in TAB_PAGES:
|
||||
if re.match(r"private System\.Windows\.Forms\.TabPage %s;$" % tp, s):
|
||||
return True
|
||||
if s == "this.%s = new System.Windows.Forms.TabPage();" % tp:
|
||||
return True
|
||||
if s.startswith("this.%s." % tp) or s == "this.%s.ResumeLayout(false);" % tp:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def ribbon_block():
|
||||
"""生成 ribbonStrip1 的 Designer 区块(tab 缩进)。"""
|
||||
t = "\t\t\t"
|
||||
L = []
|
||||
L.append(t + "// ")
|
||||
L.append(t + "// ribbonStrip1")
|
||||
L.append(t + "// ")
|
||||
|
||||
def tab(var, text):
|
||||
L.append(t + "Smart.CustomComponents.RibbonTab %s = new Smart.CustomComponents.RibbonTab();" % var)
|
||||
L.append(t + "%s.Text = \"%s\";" % (var, text))
|
||||
|
||||
def group(var, text):
|
||||
L.append(t + "Smart.CustomComponents.RibbonGroup %s = new Smart.CustomComponents.RibbonGroup();" % var)
|
||||
L.append(t + "%s.Text = \"%s\";" % (var, text))
|
||||
|
||||
def btn(field, text):
|
||||
var = "rbtn_" + field
|
||||
L.append(t + "Smart.CustomComponents.RibbonButton %s = new Smart.CustomComponents.RibbonButton();" % var)
|
||||
L.append(t + "%s.Name = \"%s\";" % (var, field))
|
||||
L.append(t + "%s.Text = \"%s\";" % (var, text))
|
||||
|
||||
def add_group(var):
|
||||
L.append(t + "rtab1.Groups.Add(%s);" % var)
|
||||
|
||||
def add_tab():
|
||||
L.append(t + "this.ribbonStrip1.Tabs.Add(rtab1);")
|
||||
|
||||
# ---- 页签1 项目 ----
|
||||
tab("rtab1", "项目")
|
||||
group("rgrpA", "项目文件")
|
||||
for f, x in BTN_PROJECT:
|
||||
btn(f, x)
|
||||
L.append(t + "rgrpA.Buttons.Add(rbtn_%s);" % f)
|
||||
add_group("rgrpA")
|
||||
group("rgrpB", "数据")
|
||||
for f, x in BTN_PROJECT2:
|
||||
btn(f, x)
|
||||
L.append(t + "rgrpB.Buttons.Add(rbtn_%s);" % f)
|
||||
add_group("rgrpB")
|
||||
add_tab()
|
||||
|
||||
# ---- 页签2 报价 ----
|
||||
tab("rtab1", "报价")
|
||||
group("rgrpA", "项目")
|
||||
for f, x in BTN_QUOTE1:
|
||||
btn(f, x)
|
||||
L.append(t + "rgrpA.Buttons.Add(rbtn_%s);" % f)
|
||||
add_group("rgrpA")
|
||||
group("rgrpB", "报价")
|
||||
for f, x in BTN_QUOTE2:
|
||||
btn(f, x)
|
||||
L.append(t + "rgrpB.Buttons.Add(rbtn_%s);" % f)
|
||||
add_group("rgrpB")
|
||||
group("rgrpC", "计算")
|
||||
for f, x in BTN_QUOTE3:
|
||||
btn(f, x)
|
||||
L.append(t + "rgrpC.Buttons.Add(rbtn_%s);" % f)
|
||||
add_group("rgrpC")
|
||||
group("rgrpD", "工具")
|
||||
for f, x in BTN_QUOTE4:
|
||||
btn(f, x)
|
||||
L.append(t + "rgrpD.Buttons.Add(rbtn_%s);" % f)
|
||||
add_group("rgrpD")
|
||||
add_tab()
|
||||
|
||||
# ---- 页签3 设置 ----
|
||||
tab("rtab1", "设置")
|
||||
group("rgrpA", "账号")
|
||||
for f, x in BTN_SET1:
|
||||
btn(f, x)
|
||||
L.append(t + "rgrpA.Buttons.Add(rbtn_%s);" % f)
|
||||
add_group("rgrpA")
|
||||
group("rgrpB", "系统")
|
||||
for f, x in BTN_SET2:
|
||||
btn(f, x)
|
||||
L.append(t + "rgrpB.Buttons.Add(rbtn_%s);" % f)
|
||||
add_group("rgrpB")
|
||||
group("rgrpC", "数据")
|
||||
for f, x in BTN_SET3:
|
||||
btn(f, x)
|
||||
L.append(t + "rgrpC.Buttons.Add(rbtn_%s);" % f)
|
||||
add_group("rgrpC")
|
||||
add_tab()
|
||||
|
||||
L.append(t + "this.ribbonStrip1.Dock = System.Windows.Forms.DockStyle.Top;")
|
||||
L.append(t + "this.ribbonStrip1.Location = new System.Drawing.Point(0, 0);")
|
||||
L.append(t + "this.ribbonStrip1.Name = \"ribbonStrip1\";")
|
||||
L.append(t + "this.ribbonStrip1.Size = new System.Drawing.Size(1884, 110);")
|
||||
L.append(t + "this.ribbonStrip1.TabIndex = 3;")
|
||||
return "\n".join(L)
|
||||
|
||||
|
||||
def replace_designer(src):
|
||||
lines = src.split("\n")
|
||||
out = []
|
||||
i = 0
|
||||
n = len(lines)
|
||||
while i < n:
|
||||
line = lines[i]
|
||||
s = line.strip()
|
||||
# 1) 按钮区块整体跳过:遇到 "// buttonN" 或 "// btnXxx" 标题行,连标题三行一起跳到区块尾
|
||||
m = re.match(r"// (button\d+|btn[^ ]+)$", s)
|
||||
if m and m.group(1) in BTN_NAMES:
|
||||
i += 3 # 跳过 // 、// buttonN、// 三行
|
||||
while i < n and lines[i].strip().startswith("this." + m.group(1) + "."):
|
||||
i += 1
|
||||
continue
|
||||
# 2) tabControl1 区块跳过(注释头三行 + this.tabControl1.* 行),替换为 ribbonStrip1 区块
|
||||
if s == "// tabControl1":
|
||||
i += 3
|
||||
while i < n and lines[i].strip().startswith("this.tabControl1"):
|
||||
i += 1
|
||||
out.append("\t\t\t// ")
|
||||
out.append("\t\t\t// ribbonStrip1")
|
||||
out.append("\t\t\t// ")
|
||||
out.append(ribbon_block())
|
||||
# 跳过后续 3 个 tabPage 区块
|
||||
for tp in TAB_PAGES:
|
||||
while i < n and lines[i].strip() != "// " + tp:
|
||||
i += 1
|
||||
if i < n:
|
||||
i += 3
|
||||
while i < n and lines[i].strip().startswith("this." + tp + "."):
|
||||
i += 1
|
||||
continue
|
||||
# 3) 其余单行删除(字段/new/suspend/resume/Controls.Add(tabControl1))
|
||||
if must_remove(line):
|
||||
i += 1
|
||||
continue
|
||||
if s == "this.Controls.Add(this.tabControl1);":
|
||||
out.append(line.replace("tabControl1", "ribbonStrip1"))
|
||||
i += 1
|
||||
continue
|
||||
out.append(line)
|
||||
i += 1
|
||||
txt = "\n".join(out)
|
||||
# new 行与字段行插入
|
||||
txt = txt.replace(
|
||||
"\t\t\tthis.tabControl1 = new System.Windows.Forms.TabControl();",
|
||||
"\t\t\tthis.ribbonStrip1 = new 主功能区();")
|
||||
txt = txt.replace(
|
||||
"\t\tprivate System.Windows.Forms.TabControl tabControl1;",
|
||||
"\t\tprivate 主功能区 ribbonStrip1;")
|
||||
return txt
|
||||
|
||||
|
||||
def replace_mainform_cs(src):
|
||||
# 1) 删 24 行 Click 挂接(button 系 12 行 + button15 lambda 段 + btn 系 5 行)
|
||||
pat_click = re.compile(
|
||||
r"[ \t]*this\.(button1|button3|button2|button5|button10|button16|button13|button18|button7|button8|button33|btn企业信息|btn备份|btn恢复|button11)\.Click \+= [^\n]*\n")
|
||||
src = pat_click.sub("", src)
|
||||
# button17 保存
|
||||
src = re.sub(r"[ \t]*this\.button17\.Click \+= \(s, e\) => SaveAllToDatabase\(\);[^\n]*\n", "", src)
|
||||
# button15 lambda 整段(从注释行到闭括号行)
|
||||
src = re.sub(
|
||||
r"[ \t]*// ===== 调母排\(button15\):普通非模态弹窗 =====\n"
|
||||
r"[ \t]*// 不自动居中、不贴靠右侧、不跟随主窗口移动、不限制大小,直接弹出。\n"
|
||||
r"[ \t]*调母排 _tiaomuForm = null;\n"
|
||||
r"[ \t]*\n"
|
||||
r"[ \t]*this\.button15\.Click \+= \(s, e\) =>\n"
|
||||
r"[ \t]*\{.*?\n"
|
||||
r"[ \t]*\};\n",
|
||||
"", src, flags=re.S)
|
||||
# 2) tabControl1.SelectedIndexChanged 联动段替换为功能区事件
|
||||
src = re.sub(
|
||||
r"[ \t]*// ===== 顶部选项卡:点击\"项目\"/\"报价\"时激活对应停靠文档窗口 =====\n"
|
||||
r"[ \t]*this\.tabControl1\.SelectedIndexChanged \+= \(s, e\) =>\n"
|
||||
r"[ \t]*\{.*?\n"
|
||||
r"[ \t]*\};\n",
|
||||
(" // ===== 功能区(ribbonStrip1):统一按钮点击路由 + 页签切换联动 =====\n"
|
||||
" this.ribbonStrip1.ButtonClick += ribbonStrip1_ButtonClick;\n"
|
||||
" this.ribbonStrip1.活动页已切换 += (s, e) =>\n"
|
||||
" {\n"
|
||||
" string tabName = (this.ribbonStrip1.ActiveTab != null) ? this.ribbonStrip1.ActiveTab.Text : \"\";\n"
|
||||
" Log(\"[TabSync] 功能区页签切换: [\" + tabName + \"]\");\n"
|
||||
" if (tabName == \"项目\" && this.w1 != null)\n"
|
||||
" {\n"
|
||||
" this.w1.Show(this.dockSurface1);\n"
|
||||
" this.w1.Activate();\n"
|
||||
" }\n"
|
||||
" else if (tabName == \"报价\" && this.w2 != null)\n"
|
||||
" {\n"
|
||||
" this.w2.Show(this.dockSurface1);\n"
|
||||
" this.w2.Activate();\n"
|
||||
" }\n"
|
||||
" // \"设置\"页只显示按钮面板,无停靠窗口\n"
|
||||
" };\n\n"),
|
||||
src, flags=re.S)
|
||||
return src
|
||||
|
||||
|
||||
def add_handlers(src):
|
||||
"""在 MainForm 类尾部(canvas:找类的最后一个方法后)插入处理器与辅助方法。找 'private void Dgv_CellValuePushed' 所在文件不适用——直接加在构造器后第一个方法前。"""
|
||||
handlers = '''
|
||||
// ============================================================
|
||||
// 功能区(ribbonStrip1)按钮路由与辅助
|
||||
// ============================================================
|
||||
|
||||
/// <summary>功能区统一按钮点击:按 RibbonButton.Name(保留原按钮字段名)分发到原处理器。</summary>
|
||||
private void ribbonStrip1_ButtonClick(object sender, Smart.CustomComponents.RibbonButtonEventArgs e)
|
||||
{
|
||||
if (e.Button == null || string.IsNullOrEmpty(e.Button.Name)) return;
|
||||
switch (e.Button.Name)
|
||||
{
|
||||
case "button1": button1_Click(sender, e); break; // 新建项目
|
||||
case "button2": button2_Click(sender, e); break; // 修改项目(项目页)
|
||||
case "button3": button3_Click(sender, e); break; // 删除项目
|
||||
case "button4": break; // 打开项目(项目页,原无处理器)
|
||||
case "button5": button5_Click(sender, e); break; // 复制项目
|
||||
case "button7": button7_Click(sender, e); break; // 导出项目
|
||||
case "button8": button8_Click(sender, e); break; // 导入项目
|
||||
case "button9": break; // 打开项目(报价页,原无处理器)
|
||||
case "button10": button10_Click(sender, e); break; // 修改项目(报价页)
|
||||
case "button11": button11_Click(sender, e); break; // CAD扒图
|
||||
case "button13": button13_Click(sender, e); break; // 调系数
|
||||
case "button15": 打开调母排窗口(); break; // 调母排
|
||||
case "button16": button16_Click(sender, e); break; // 甲方报表
|
||||
case "button17": SaveAllToDatabase(); break; // 保存
|
||||
case "button18": button18_Click(sender, e); break; // 导入报价
|
||||
case "button29": break; // 模板费用(原无处理器)
|
||||
case "button33": button33_Click(sender, e); break; // 元件排序
|
||||
case "btn登录": break; // 登录(原无处理器)
|
||||
case "btn修改密码": break; // 修改密码(原无处理器)
|
||||
case "btn企业信息": btn企业信息_Click(sender, e); break; // 企业信息
|
||||
case "btn会员信息": break; // 会员信息(原无处理器)
|
||||
case "btn保存布局": btn保存布局_Click(sender, e); break; // 保存布局
|
||||
case "btn备份": btn备份_Click(sender, e); break; // 备份
|
||||
case "btn恢复": btn恢复_Click(sender, e); break; // 恢复
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>按 Name 在功能区三层集合里找 RibbonButton(找不到返回 null)。</summary>
|
||||
private Smart.CustomComponents.RibbonButton FindRibbonButton(string name)
|
||||
{
|
||||
if (this.ribbonStrip1 == null) return null;
|
||||
foreach (Smart.CustomComponents.RibbonTab tab in this.ribbonStrip1.Tabs)
|
||||
foreach (Smart.CustomComponents.RibbonGroup grp in tab.Groups)
|
||||
foreach (Smart.CustomComponents.RibbonButton btn in grp.Buttons)
|
||||
if (btn.Name == name) return btn;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>调母排(button15):普通非模态弹窗(原构造器内 lambda 抽出)。
|
||||
/// 不自动居中、不贴靠右侧、不跟随主窗口移动、不限制大小,直接弹出。</summary>
|
||||
private 调母排 _调母排窗;
|
||||
private void 打开调母排窗口()
|
||||
{
|
||||
if (_调母排窗 != null && !_调母排窗.IsDisposed)
|
||||
{
|
||||
_调母排窗.Activate();
|
||||
return;
|
||||
}
|
||||
if (string.IsNullOrWhiteSpace(_currentProjectId))
|
||||
{
|
||||
MessageBox.Show(this, "请先在项目列表双击打开一个项目。", "调母排",
|
||||
MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
return;
|
||||
}
|
||||
_调母排窗 = new 调母排(this);
|
||||
// 不设置 StartPosition/Location/Size/MaximumSize/MinimumSize,使用窗体自身默认值
|
||||
_调母排窗.Show(this);
|
||||
_调母排窗.FormClosed += (s2, e2) => { _调母排窗 = null; };
|
||||
}
|
||||
|
||||
/// <summary>项目菜单页"修改项目"按钮(项目页 button2):无条件可用,补路由。</summary>
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
修改项目入口();
|
||||
}
|
||||
'''
|
||||
# button2_Click 原本存在?若存在会重复定义——先检查
|
||||
if "private void button2_Click" in src and "ribbonStrip1_ButtonClick" not in src:
|
||||
# 已有 button2_Click,去掉补丁里的重复定义
|
||||
handlers = handlers.replace('''
|
||||
/// <summary>项目菜单页"修改项目"按钮(项目页 button2):无条件可用,补路由。</summary>
|
||||
private void button2_Click(object sender, EventArgs e)
|
||||
{
|
||||
修改项目入口();
|
||||
}
|
||||
''', "")
|
||||
# 插入点:构造器结束后(锚点在构造器内,往后找第一个方法级结束括号 \t\t})
|
||||
anchor = " // ===== 监听主区域 tabControl2 切换"
|
||||
idx = src.find(anchor)
|
||||
if idx < 0:
|
||||
raise SystemExit("MainForm.cs 找不到插入锚点")
|
||||
m = re.search(r"\n\t\t\}\n", src[idx:])
|
||||
if not m:
|
||||
raise SystemExit("MainForm.cs 找不到构造器结束位置")
|
||||
pos = idx + m.end()
|
||||
return src[:pos] + handlers + "\n" + src[pos:]
|
||||
|
||||
|
||||
def replace_zhuwindow(src):
|
||||
# UpdateButton10State: button10 → FindRibbonButton
|
||||
src = src.replace(
|
||||
" this.button10.Enabled = onQuotePage && hasProject;",
|
||||
" var rb10 = FindRibbonButton(\"button10\");\n"
|
||||
" if (rb10 != null) rb10.Enabled = onQuotePage && hasProject;")
|
||||
# EnterProjectCore: tabControl1.SelectedTab = tabPage2
|
||||
src = src.replace(
|
||||
" this.tabControl1.SelectedTab = this.tabPage2;\n"
|
||||
" Log(\"[EnterProjectCore] 已设 tabControl1.SelectedTab = tabPage2\");",
|
||||
" int 报价页idx = this.ribbonStrip1.页索引(\"报价\");\n"
|
||||
" if (报价页idx >= 0) this.ribbonStrip1.设活动页(报价页idx);\n"
|
||||
" Log(\"[EnterProjectCore] 已设功能区活动页=报价\");")
|
||||
return src
|
||||
|
||||
|
||||
def replace_compat(src):
|
||||
# 删 tabPage1/2/8 三个兼容属性
|
||||
src = re.sub(
|
||||
r"[ \t]*// ---- 旧页签名 → 新控件别名(旧代码 this\.tabPageN 继续可用)----\n"
|
||||
r".*?public DockContent tabPage3 \{ get \{ return this\.w1; \} \}",
|
||||
" // ---- 旧页签名 → 新控件别名(旧代码 this.tabPageN 继续可用)----\n\n"
|
||||
" /// <summary>旧左侧\"项目\"页 → 停靠窗口\"项目\"。</summary>\n"
|
||||
" public DockContent tabPage3 { get { return this.w1; } }",
|
||||
src, flags=re.S)
|
||||
return src
|
||||
|
||||
|
||||
def main():
|
||||
import os
|
||||
# 幂等保护
|
||||
d = read(os.path.join(ROOT, "MainForm.Designer.cs"))
|
||||
if "ribbonStrip1" in d:
|
||||
print("Designer.cs 已含 ribbonStrip1,跳过")
|
||||
else:
|
||||
write(os.path.join(ROOT, "MainForm.Designer.cs"), replace_designer(d))
|
||||
print("Designer.cs 改写完成")
|
||||
|
||||
m = read(os.path.join(ROOT, "MainForm.cs"))
|
||||
if "ribbonStrip1_ButtonClick(object sender" not in m:
|
||||
m2 = replace_mainform_cs(m)
|
||||
m2 = add_handlers(m2)
|
||||
write(os.path.join(ROOT, "MainForm.cs"), m2)
|
||||
print("MainForm.cs 改写完成")
|
||||
else:
|
||||
print("MainForm.cs 已改,跳过")
|
||||
|
||||
z = read(os.path.join(ROOT, "主窗口.cs"))
|
||||
if "FindRibbonButton(\"button10\")" not in z:
|
||||
write(os.path.join(ROOT, "主窗口.cs"), replace_zhuwindow(z))
|
||||
print("主窗口.cs 改写完成")
|
||||
else:
|
||||
print("主窗口.cs 已改,跳过")
|
||||
|
||||
c = read(os.path.join(ROOT, "MainForm.旧页签兼容.cs"))
|
||||
if "tabPage2 { get" in c:
|
||||
write(os.path.join(ROOT, "MainForm.旧页签兼容.cs"), replace_compat(c))
|
||||
print("旧页签兼容.cs 改写完成")
|
||||
else:
|
||||
print("旧页签兼容.cs 已改,跳过")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,23 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# 验证 清理代理与WebView进程 的 PowerShell 脚本(C# 里用 Base64 UTF-16LE 传递)
|
||||
# 此处以完全相同的方式构建并执行,确认语法正确、能正常运行并输出计数
|
||||
import base64
|
||||
import subprocess
|
||||
|
||||
ps = (
|
||||
"$n=0; "
|
||||
"Get-CimInstance Win32_Process -Filter \"Name='python.exe' OR Name='pythonw.exe' OR Name='msedgewebview2.exe'\" | "
|
||||
"Where-Object { $_.ProcessId -ne $pid -and ( "
|
||||
"$_.CommandLine -like '*proxy_server.py*' -or "
|
||||
"($_.Name -eq 'msedgewebview2.exe' -and $_.CommandLine -like '*成套报价软件.exe.WebView2*') ) } | "
|
||||
"ForEach-Object { try { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue; $n++ } catch {} }; "
|
||||
"Write-Output $n"
|
||||
)
|
||||
enc = base64.b64encode(ps.encode("utf-16-le")).decode()
|
||||
r = subprocess.run(
|
||||
["powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-EncodedCommand", enc],
|
||||
capture_output=True, text=True, timeout=30,
|
||||
)
|
||||
print("退出码:", r.returncode)
|
||||
print("输出(清理数):", r.stdout.strip())
|
||||
print("错误:", r.stderr.strip()[:300] if r.stderr.strip() else "无")
|
||||
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""用 C# 同款拆分规则验证正泰格式的型号规格拆分(拆规格与名称)。"""
|
||||
import re
|
||||
|
||||
def 拆(原始):
|
||||
# 找最长连续中文段(>=2字)
|
||||
best = None
|
||||
for m in re.finditer(r'[\u4e00-\u9fa5]{2,}', 原始):
|
||||
if best is None or len(m.group()) > len(best.group()):
|
||||
best = m
|
||||
if best:
|
||||
名称 = best.group()
|
||||
规格 = (原始[:best.start()] + ' ' + 原始[best.end():]).strip()
|
||||
规格 = re.sub(r'\s+', ' ', 规格).strip(' -/')
|
||||
return 规格, 名称
|
||||
return 原始.strip(), ''
|
||||
|
||||
cases = [
|
||||
"CA1-100/4300B/40A 断路器",
|
||||
"CH1-63C16/1P",
|
||||
"DJMB2 220/36 0.3kVA",
|
||||
"RT12-63/4P 40A",
|
||||
"CM3-63M/33002/50A 塑壳断路器",
|
||||
"LAM-C40/4P",
|
||||
"CH1L-32C20/1N 剩余电流断路器",
|
||||
]
|
||||
for c in cases:
|
||||
规格, 名称 = 拆(c)
|
||||
print(repr(c), '→ 规格:', repr(规格), ' 名称:', repr(名称))
|
||||
@@ -0,0 +1,193 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""触发器总开关+批量刷新:
|
||||
①计算引擎:触发器统一加 WHEN 开关=0 条件;新增 开关表/开关设/批量刷新方法;重建时确保开关表存在
|
||||
②管线(图号/箱号粘贴、删除):开关=1 → 批量写 → 刷新图号全部计算 → 开关=0 (全在原事务内)
|
||||
幂等。"""
|
||||
import io
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
|
||||
n = 0
|
||||
|
||||
def edit(path, pairs, tag):
|
||||
global s, n
|
||||
for old, new in pairs:
|
||||
if old not in s:
|
||||
if new in s:
|
||||
print(tag, "已有(跳过)")
|
||||
continue
|
||||
raise AssertionError(tag + " 锚点缺失: " + old[:70])
|
||||
s = s.replace(old, new, 1)
|
||||
n += 1
|
||||
io.open(path, "w", encoding="utf-8-sig").write(s)
|
||||
print(tag, "ok")
|
||||
|
||||
p = ROOT + r"\计算引擎.cs"
|
||||
s = io.open(p, encoding="utf-8-sig").read()
|
||||
|
||||
# 1) 开关常量与开关表
|
||||
@@SKIP@@
|
||||
public const string 触发器前缀 = "trgjs_";''',
|
||||
''' /// <summary>触发器名前缀(重建时按前缀全清)。</summary>
|
||||
public const string 触发器前缀 = "trgjs_";
|
||||
|
||||
/// <summary>触发器总开关条件(批量操作时设 开关=1 跳过触发器体,提交前批量刷新+设回 0)。</summary>
|
||||
public const string 开关条件 = "(SELECT 开关 FROM _触发器开关 WHERE id=1)=0";''')
|
||||
|
||||
# 上面的辅助不适用,直接顺序替换
|
||||
def rep2(old, new):
|
||||
global s, n
|
||||
if old not in s:
|
||||
if new in s:
|
||||
print("跳过(已改)")
|
||||
return
|
||||
raise AssertionError("锚点缺失: " + old[:70])
|
||||
s = s.replace(old, new, 1)
|
||||
n += 1
|
||||
|
||||
rep2(''' /// <summary>触发器名前缀(重建时按前缀全清)。</summary>
|
||||
public const string 触发器前缀 = "trgjs_";''',
|
||||
''' /// <summary>触发器名前缀(重建时按前缀全清)。</summary>
|
||||
public const string 触发器前缀 = "trgjs_";
|
||||
|
||||
/// <summary>触发器总开关条件:批量操作时置 开关=1,触发器整体短路,提交前批量刷新后置回 0。</summary>
|
||||
public const string 开关条件 = "(SELECT 开关 FROM _触发器开关 WHERE id=1)=0";''')
|
||||
|
||||
# 2) EnsureFormulaTable 里顺带建开关表
|
||||
rep2(''' using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"CREATE TABLE IF NOT EXISTS \\"" + 公式表名 + "\\" (" +
|
||||
"层级 TEXT, 目标列 TEXT, 表达式 TEXT, 排序 INTEGER, 启用 INTEGER DEFAULT 1);", conn))
|
||||
cmd.ExecuteNonQuery();''',
|
||||
''' using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"CREATE TABLE IF NOT EXISTS \\"" + 公式表名 + "\\" (" +
|
||||
"层级 TEXT, 目标列 TEXT, 表达式 TEXT, 排序 INTEGER, 启用 INTEGER DEFAULT 1);", conn))
|
||||
cmd.ExecuteNonQuery();
|
||||
// 开关表放项目库(触发器同库),由 重建项目全部触发器 保证存在''')
|
||||
|
||||
# 3) 所有触发器 CREATE 加 WHEN 开关条件
|
||||
rep2(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_u AFTER UPDATE OF " + of + " ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet +
|
||||
" WHERE rowid=NEW.rowid; " + 刷箱号 + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_i AFTER INSERT ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet +
|
||||
" WHERE rowid=NEW.rowid; " + 刷箱号 + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_d AFTER DELETE ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW BEGIN " + 刷箱号 + 刷图号 + "END;");''',
|
||||
''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_u AFTER UPDATE OF " + of + " ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet +
|
||||
" WHERE rowid=NEW.rowid; " + 刷箱号 + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_i AFTER INSERT ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN UPDATE \\"" + 元件表 + "\\" SET " + selfSet +
|
||||
" WHERE rowid=NEW.rowid; " + 刷箱号 + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(元件表) + "_d AFTER DELETE ON \\"" +
|
||||
元件表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN " + 刷箱号 + 刷图号 + "END;");''')
|
||||
|
||||
rep2(''' Exec(conn, tx, "DROP TRIGGER IF EXISTS " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(箱号ID) + "_u;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(箱号ID) +
|
||||
"_u AFTER UPDATE OF " + of + " ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" +
|
||||
箱号ID.Replace("'", "''") + "' BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew +
|
||||
" WHERE rowid=NEW.rowid; UPDATE \\"" + 图号表名 + "\\" SET " + thSet +
|
||||
" WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "'; END;");''',
|
||||
''' Exec(conn, tx, "DROP TRIGGER IF EXISTS " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(箱号ID) + "_u;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_r_" + Safe(箱号ID) +
|
||||
"_u AFTER UPDATE OF " + of + " ON \\"" + 箱号表 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" +
|
||||
箱号ID.Replace("'", "''") + "' AND " + 开关条件 + " BEGIN UPDATE \\"" + 箱号表 + "\\" SET " + xhSetNew +
|
||||
" WHERE rowid=NEW.rowid; UPDATE \\"" + 图号表名 + "\\" SET " + thSet +
|
||||
" WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "'; END;");''')
|
||||
|
||||
rep2(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW BEGIN " + 刷图号 + "END;");''',
|
||||
''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_i AFTER INSERT ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN " + 刷图号 + "END;");
|
||||
Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + Safe(箱号表) + "_d AFTER DELETE ON \\"" +
|
||||
箱号表 + "\\" FOR EACH ROW WHEN " + 开关条件 + " BEGIN " + 刷图号 + "END;");''')
|
||||
|
||||
rep2(''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + "th_" + suffix + "_u AFTER UPDATE OF \\"" +
|
||||
图号输入列[0] + "\\" ON \\"" + 图号表名 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" +
|
||||
图号ID.Replace("'", "''") + "' BEGIN UPDATE \\"" + 图号表名 + "\\" SET " + thSetNew +
|
||||
" WHERE rowid=NEW.rowid; END;");''',
|
||||
''' Exec(conn, tx, "CREATE TRIGGER " + 触发器前缀 + "th_" + suffix + "_u AFTER UPDATE OF \\"" +
|
||||
图号输入列[0] + "\\" ON \\"" + 图号表名 + "\\" FOR EACH ROW WHEN NEW.\\"内部ID\\"='" +
|
||||
图号ID.Replace("'", "''") + "' AND " + 开关条件 + " BEGIN UPDATE \\"" + 图号表名 + "\\" SET " + thSetNew +
|
||||
" WHERE rowid=NEW.rowid; END;");''')
|
||||
|
||||
# 4) 重建项目全部触发器:确保开关表+默认行
|
||||
rep2(''' var old = new List<string>();''',
|
||||
''' using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"CREATE TABLE IF NOT EXISTS _触发器开关 (id INTEGER PRIMARY KEY, 开关 INTEGER DEFAULT 0);", conn, tx))
|
||||
cmd.ExecuteNonQuery();
|
||||
Exec(conn, tx, "INSERT OR IGNORE INTO _触发器开关 (id, 开关) VALUES (1, 0);");
|
||||
|
||||
var old = new List<string>();''')
|
||||
|
||||
# 5) 批量刷新方法(管线提交前调用):按表索引刷新该图号全部计算
|
||||
rep2(''' /// <summary>新表(管线内建表后)即时补触发器——只处理该表自身。</summary>''',
|
||||
''' /// <summary>
|
||||
/// 批量刷新某图号的全部计算(触发器关闭期间用):元件表派生列→箱号行聚合→图号行聚合。
|
||||
/// 全部为集合 SQL,每表一条 UPDATE;开关由调用方控制。
|
||||
/// </summary>
|
||||
public static void 批量刷新图号计算(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx,
|
||||
string 图号ID, string 图号表名, System.Data.SQLite.SQLiteConnection fakConn)
|
||||
{
|
||||
var 元件公式 = 加载公式(fakConn, "元件");
|
||||
var 箱号公式 = 加载公式(fakConn, "箱号");
|
||||
var 图号公式 = 加载公式(fakConn, "图号");
|
||||
|
||||
string xhTbl = null;
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT 实际表名 FROM 表索引 WHERE 类型='箱号' AND 图号=@th LIMIT 1;", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@th", 图号ID);
|
||||
object o = cmd.ExecuteScalar();
|
||||
xhTbl = o == null || o == DBNull.Value ? null : o.ToString();
|
||||
}
|
||||
if (string.IsNullOrEmpty(xhTbl)) return;
|
||||
|
||||
// 1) 每张元件表:派生列集合 UPDATE
|
||||
var yj表们 = new List<string>();
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT 实际表名 FROM 表索引 WHERE 类型='元件' AND 图号=@th;", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@th", 图号ID);
|
||||
using (var r = cmd.ExecuteReader())
|
||||
while (r.Read()) yj表们.Add(r.GetString(0));
|
||||
}
|
||||
string yjSet = 生成Set片段(元件公式, "ROW", null);
|
||||
foreach (var yj in yj表们)
|
||||
Exec(conn, tx, "UPDATE \\"" + yj + "\\" SET " + yjSet + ";");
|
||||
|
||||
// 2) 箱号每行:聚合(行自己的元件表 SUM)——行级一条 UPDATE
|
||||
string xhSet = 生成Set片段(箱号公式, "ROW", null); // 由下方逐行替换 SUM
|
||||
// 逐行:SUM 需要按行的元件表,取 表索引(元件) 列表
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"SELECT 箱号, 实际表名 FROM 表索引 WHERE 类型='元件' AND 图号=@th;", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@th", 图号ID);
|
||||
using (var r = cmd.ExecuteReader())
|
||||
{
|
||||
var pairs = new List<string[]>();
|
||||
while (r.Read()) pairs.Add(new[] { r.IsDBNull(0) ? "" : r.GetString(0), r.IsDBNull(1) ? "" : r.GetString(1) });
|
||||
foreach (var pr in pairs)
|
||||
{
|
||||
string 行Set = 生成Set片段(箱号公式, "ROW", pr[1]);
|
||||
Exec(conn, tx, "UPDATE \\"" + xhTbl + "\\" SET " + 行Set + " WHERE \\"内部ID\\"='" + pr[0].Replace("'", "''") + "';");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3) 图号行:聚合(FROM 箱号表)
|
||||
string thSet = 生成Set片段(图号公式, "ROW", xhTbl);
|
||||
Exec(conn, tx, "UPDATE \\"" + 图号表名 + "\\" SET " + thSet + " WHERE \\"内部ID\\"='" + 图号ID.Replace("'", "''") + "';");
|
||||
}
|
||||
|
||||
/// <summary>开关设值(1=批量模式跳过触发器,0=恢复自动触发)。</summary>
|
||||
public static void 开关设(System.Data.SQLite.SQLiteConnection conn, System.Data.SQLite.SQLiteTransaction tx, int 值)
|
||||
{
|
||||
Exec(conn, tx, "UPDATE _触发器开关 SET 开关=" + 值 + " WHERE id=1;");
|
||||
}
|
||||
|
||||
/// <summary>新表(管线内建表后)即时补触发器——只处理该表自身。</summary>''')
|
||||
|
||||
io.open(p, "w", encoding="utf-8-sig").write(s)
|
||||
print("计算引擎 完成", n, "处")
|
||||
@@ -0,0 +1,147 @@
|
||||
# csc 编译验证(UTF-8)
|
||||
/target:winexe
|
||||
/platform:x86
|
||||
/utf8output
|
||||
/nologo
|
||||
/warn:0
|
||||
/win32icon:app.ico
|
||||
/out:bin\Debug\成套报价软件.exe
|
||||
/r:System.dll
|
||||
/r:System.Core.dll
|
||||
/r:System.Data.dll
|
||||
/r:System.Drawing.dll
|
||||
/r:System.Windows.Forms.dll
|
||||
/r:System.Xml.dll
|
||||
/r:System.IO.Compression.dll
|
||||
/r:System.IO.Compression.FileSystem.dll
|
||||
/r:"C:\Program Files (x86)\SharpDevelop\5.2\bin\Smart.CustomComponents.dll"
|
||||
/r:"C:\Program Files (x86)\SharpDevelop\5.2\bin\WeifenLuo.WinFormsUI.Docking.dll"
|
||||
/r:"C:\Program Files (x86)\SharpDevelop\5.2\bin\WeifenLuo.WinFormsUI.Docking.ThemeVS2015.dll"
|
||||
/r:"lib\AdvancedDataGridView.dll"
|
||||
/r:"lib\Microsoft.Web.WebView2.Core.dll"
|
||||
/r:"lib\Microsoft.Web.WebView2.WinForms.dll"
|
||||
/r:"C:\Program Files (x86)\SharpDevelop\5.2\bin\System.Data.SQLite.dll"
|
||||
/r:"lib\NPOI.dll"
|
||||
/r:"lib\NPOI.OOXML.dll"
|
||||
/r:"lib\NPOI.OpenXml4Net.dll"
|
||||
/r:"lib\NPOI.OpenXmlFormats.dll"
|
||||
/r:"lib\DotNetZip.dll"
|
||||
/r:"lib\ICSharpCode.SharpZipLib.dll"
|
||||
/r:"lib\unvell.ReoGrid.dll"
|
||||
/r:"lib\CJZZ.Controls.dll"
|
||||
/r:System.Web.Extensions.dll
|
||||
/r:System.Configuration.dll
|
||||
MainForm.cs
|
||||
计算引擎.cs
|
||||
编辑直写库.cs
|
||||
MainForm.Designer.cs
|
||||
Program.cs
|
||||
Properties\AssemblyInfo.cs
|
||||
主窗口.cs
|
||||
主窗口.菜单与库.cs
|
||||
主窗口.公式重算.cs
|
||||
主窗口.母排与备份.cs
|
||||
备份目标选择.cs
|
||||
会员安全客户端.cs
|
||||
主窗口.状态栏.cs
|
||||
主窗口.项目导入导出.cs
|
||||
主窗口.SQLite加载.cs
|
||||
MainForm.控件别名.cs
|
||||
MainForm.旧页签兼容.cs
|
||||
MainForm.停靠布局.cs
|
||||
价格格式化.cs
|
||||
xiangmu\新建项目.cs
|
||||
xiangmu\新建项目.Designer.cs
|
||||
xiangmu\修改项目.cs
|
||||
xiangmu\修改项目.Designer.cs
|
||||
xiangmu\甲方报表.cs
|
||||
xiangmu\甲方报表.Designer.cs
|
||||
xiangmu\项目云.cs
|
||||
xiangmu\调系数.cs
|
||||
xiangmu\调系数.Designer.cs
|
||||
xiangmu\导入报价.cs
|
||||
xiangmu\导入报价.Designer.cs
|
||||
xiangmu\元件排序.cs
|
||||
xiangmu\元件排序.Designer.cs
|
||||
xiangmu\调母排.cs
|
||||
xiangmu\调母排.Designer.cs
|
||||
xiangmu\批量插入询问.cs
|
||||
xiangmu\批量插入询问.Designer.cs
|
||||
xiangmu\费用模板.cs
|
||||
xiangmu\费用模板.Designer.cs
|
||||
shezhi\企业信息.cs
|
||||
shezhi\企业信息.Designer.cs
|
||||
shezhi\关于软件.cs
|
||||
shezhi\关于软件.Designer.cs
|
||||
shezhi\许可证文本.cs
|
||||
shezhi\许可证文本.Designer.cs
|
||||
登录.cs
|
||||
登录.Designer.cs
|
||||
会员服务Host.cs
|
||||
OpenClawHost.cs
|
||||
AI助手接口.cs
|
||||
AI助手设置.cs
|
||||
AI助手设置.Designer.cs
|
||||
扒图\PaTypes.cs
|
||||
扒图\CadBridge.cs
|
||||
扒图\CadViewerWindow.cs
|
||||
扒图\CadViewerWindow.Designer.cs
|
||||
扒图\PdfBridge.cs
|
||||
扒图\PdfViewerWindow.cs
|
||||
扒图\PdfiumNative.cs
|
||||
扒图\PdfDocument.cs
|
||||
扒图\OcrClient.cs
|
||||
扒图\GlmOcrClient.cs
|
||||
扒图\QianfanOcrClient.cs
|
||||
扒图\UapisOcrClient.cs
|
||||
扒图\UmiOcrClient.cs
|
||||
扒图\UmiOcrHost.cs
|
||||
扒图\UmiOcrLauncher.cs
|
||||
扒图\OcrSettingsForm.cs
|
||||
扒图\OcrSettingsForm.Designer.cs
|
||||
扒图\Cad扒图窗口.cs
|
||||
扒图\Pdf扒图窗口.cs
|
||||
扒图\CadQuoteWindow.cs
|
||||
扒图\CadQuoteWindow.Designer.cs
|
||||
扒图\PdfQuoteWindow.cs
|
||||
扒图\PdfQuoteWindow.Designer.cs
|
||||
扒图\Cad\Form3.cs
|
||||
扒图\Cad\Form3.ui.cs
|
||||
扒图\Cad\新建项目.cs
|
||||
扒图\Cad\新建项目.ui.cs
|
||||
扒图\Pdf\Form3.cs
|
||||
扒图\Pdf\Form3.Designer.cs
|
||||
扒图\Pdf\新建项目.cs
|
||||
扒图\Pdf\新建项目.Designer.cs
|
||||
会员信息.cs
|
||||
会员信息.Designer.cs
|
||||
修改密码.cs
|
||||
修改密码.Designer.cs
|
||||
ku\用户库元件编辑.cs
|
||||
ku\用户库元件编辑.Designer.cs
|
||||
GCS母排计算\GCS母排计算窗口.cs
|
||||
GCS母排计算\GCS母排计算窗口.Designer.cs
|
||||
GCS母排计算\GCS推荐铜排窗口.cs
|
||||
GCS母排计算\GCS推荐铜排窗口.Designer.cs
|
||||
DockWindows\DockWindow\DockWindow.cs
|
||||
DockWindows\DockWindow\DockWindow.Designer.cs
|
||||
DockWindows\DockWindow2\DockWindow2.cs
|
||||
DockWindows\DockWindow2\DockWindow2.Designer.cs
|
||||
DockWindows\DockWindow3\DockWindow3.cs
|
||||
DockWindows\DockWindow3\DockWindow3.Designer.cs
|
||||
DockWindows\DockWindow4\DockWindow4.cs
|
||||
DockWindows\DockWindow4\DockWindow4.Designer.cs
|
||||
DockWindows\DockWindow5\DockWindow5.cs
|
||||
DockWindows\DockWindow5\DockWindow5.Designer.cs
|
||||
DockWindows\DockWindow6\DockWindow6.cs
|
||||
DockWindows\DockWindow6\DockWindow6.Designer.cs
|
||||
DockWindows\DockWindow7\DockWindow7.cs
|
||||
DockWindows\DockWindow7\DockWindow7.Designer.cs
|
||||
DockWindows\DockWindow8\DockWindow8.cs
|
||||
DockWindows\DockWindow8\DockWindow8.Designer.cs
|
||||
DockWindows\DockWindow9\DockWindow9.cs
|
||||
DockWindows\DockWindow9\DockWindow9.Designer.cs
|
||||
DockWindows\DockWindow10\DockWindow10.cs
|
||||
DockWindows\DockWindow10\DockWindow10.Designer.cs
|
||||
DockWindows\MainForm.dockSurface1.DockWindows.cs
|
||||
报价公式.cs
|
||||
@@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""管线接入触发器:①图号粘贴管线建 xh/yj 表后即建触发器 ②管尾 RecalcAllFromDatabase
|
||||
→ 计算引擎.刷新项目汇总 ③管线开头加载公式。幂等(带标记检查)。"""
|
||||
import io
|
||||
import re
|
||||
|
||||
P = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件\主窗口.菜单与库.cs"
|
||||
s = io.open(P, encoding="utf-8-sig").read()
|
||||
n = 0
|
||||
|
||||
# 0) 管线开头加载公式(conn.Open 后、journal_mode 日志后):图号粘贴专属段
|
||||
anchor = 'Log("[粘贴图号] 库=" + dbPath + " journal_mode=" + dcmd.ExecuteScalar());'
|
||||
add = anchor + '''
|
||||
// ★ 公式加载(Excel 式公式表):触发器生成与管线内联计算共用
|
||||
using (var fakConn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(
|
||||
System.IO.Path.Combine(System.IO.Path.GetDirectoryName(dbPath), "fak.db"))))
|
||||
{
|
||||
fakConn.Open();
|
||||
计算引擎.EnsureFormulaTable(fakConn);
|
||||
_公式_元件 = 计算引擎.加载公式(fakConn, "元件");
|
||||
_公式_箱号 = 计算引擎.加载公式(fakConn, "箱号");
|
||||
_公式_图号 = 计算引擎.加载公式(fakConn, "图号");
|
||||
}'''
|
||||
if "_公式_元件 = 计算引擎.加载公式" not in s:
|
||||
assert anchor in s, "管线开头锚点缺失"
|
||||
s = s.replace(anchor, add, 1)
|
||||
n += 1
|
||||
|
||||
# 1) CREATE xh + 表索引登记后 → 建箱号表触发器
|
||||
a1 = '''INSERT INTO 表索引 (类型, 图号, 箱号, 实际表名) VALUES ('箱号', @th, '', @real);", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@th", nt);
|
||||
cmd.Parameters.AddWithValue("@real", xhTbl);
|
||||
cmd.ExecuteNonQuery();
|
||||
}'''
|
||||
add1 = a1 + '''
|
||||
// ★ 建箱号表触发器:INSERT/UPDATE/DELETE 自动算行内公式+刷新图号聚合
|
||||
计算引擎.建箱号表触发器(conn, tx, xhTbl, nt, TuhaoTableName, _公式_箱号, _公式_图号);'''
|
||||
if "计算引擎.建箱号表触发器(conn, tx, xhTbl" not in s:
|
||||
assert a1 in s, "xh 锚点缺失"
|
||||
s = s.replace(a1, add1, 1)
|
||||
n += 1
|
||||
|
||||
# 2) 图号管线 CREATE yj + 表索引登记后 → 建元件表触发器
|
||||
a2 = '''INSERT INTO 表索引 (类型, 图号, 箱号, 实际表名) VALUES ('元件', @th, @xh, @real);", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@th", nt);
|
||||
cmd.Parameters.AddWithValue("@xh", nx);
|
||||
cmd.Parameters.AddWithValue("@real", yjTbl);
|
||||
cmd.ExecuteNonQuery();
|
||||
}'''
|
||||
add2 = a2 + '''
|
||||
// ★ 建元件表触发器:INSERT/UPDATE/DELETE 自动算派生价+刷新箱号/图号聚合
|
||||
计算引擎.建元件表触发器(conn, tx, yjTbl, xhTbl, nx, nt, TuhaoTableName,
|
||||
_公式_元件, _公式_箱号, _公式_图号);'''
|
||||
if "计算引擎.建元件表触发器(conn, tx, yjTbl" not in s:
|
||||
assert a2 in s, "yj 锚点缺失"
|
||||
s = s.replace(a2, add2, 1)
|
||||
n += 1
|
||||
|
||||
# 3) 管尾 RecalcAllFromDatabase(pid) (图号粘贴 657) → 刷新项目汇总
|
||||
old3 = ''' // ★ 全量重算:箱号聚合元件价格、图号聚合箱号、项目聚合图号
|
||||
// (管线 INSERT 的公式列是 0,必须重算;纯数据库操作,后台线程安全)
|
||||
RecalcAllFromDatabase(pid);'''
|
||||
new3 = ''' // ★ 触发器已在 INSERT 时自动算好派生列与箱号/图号聚合;只补跨库项目汇总
|
||||
计算引擎.刷新项目汇总(dbPath, pid, TuhaoTableName);'''
|
||||
if old3 in s:
|
||||
s = s.replace(old3, new3, 1)
|
||||
n += 1
|
||||
|
||||
io.open(P, "w", encoding="utf-8-sig").write(s)
|
||||
print("图号管线接入完成", n, "处")
|
||||
@@ -0,0 +1,138 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""第二批接线:①箱号粘贴/方案库管尾 RecalcAllFromDatabase → 刷新项目汇总
|
||||
②PasteYuanjianRowsToDb 建新表后建触发器 ③RecalcAllAfterReload 落库段退役
|
||||
④SaveAllQuoteTablesIfDirty 置空(保留 EndEdit) ⑤SaveAllToDatabaseSilent 置空
|
||||
⑥EnterProjectCore 挂触发器重建 ⑦保存按钮改实时保存提示"""
|
||||
import io
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
|
||||
n = 0
|
||||
|
||||
def edit(path, pairs, tag):
|
||||
global n
|
||||
s = io.open(path, encoding="utf-8-sig").read()
|
||||
for old, new in pairs:
|
||||
if new in s and old not in s:
|
||||
continue
|
||||
assert old in s, tag + " 锚点缺失: " + old[:60]
|
||||
s = s.replace(old, new, 1)
|
||||
n += 1
|
||||
io.open(path, "w", encoding="utf-8-sig").write(s)
|
||||
print(tag, "ok")
|
||||
|
||||
# ①②③ 菜单与库.cs
|
||||
p1 = ROOT + r"\主窗口.菜单与库.cs"
|
||||
edit(p1, [
|
||||
# 箱号粘贴管尾
|
||||
(''' // ★ 全量重算:箱号聚合元件价格、图号聚合箱号(管线 INSERT 公式列是 0)
|
||||
RecalcAllFromDatabase(pid);''',
|
||||
''' // ★ 触发器已自动算好箱号/图号聚合;只补跨库项目汇总
|
||||
计算引擎.刷新项目汇总(dbPath, pid, TuhaoTableName);'''),
|
||||
# 方案库两处
|
||||
(''' RecalcAllFromDatabase(_currentProjectId); // ★ 重算箱号/图号/项目汇总价''',
|
||||
''' 计算引擎.刷新项目汇总(GetQuoteDbPath(_currentProjectId), _currentProjectId, TuhaoTableName); // 触发器已算明细,补跨库项目汇总'''),
|
||||
# PasteYuanjianRowsToDb 建新表后建触发器
|
||||
(''' using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"INSERT INTO 表索引 (类型, 图号, 箱号, 实际表名) VALUES ('元件', @th, @xh, @real);", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@th", tuhao);
|
||||
cmd.Parameters.AddWithValue("@xh", xhId);
|
||||
cmd.Parameters.AddWithValue("@real", tblName);
|
||||
cmd.ExecuteNonQuery();
|
||||
}''',
|
||||
''' using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"INSERT INTO 表索引 (类型, 图号, 箱号, 实际表名) VALUES ('元件', @th, @xh, @real);", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@th", tuhao);
|
||||
cmd.Parameters.AddWithValue("@xh", xhId);
|
||||
cmd.Parameters.AddWithValue("@real", tblName);
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
// ★ 新元件表:建公式触发器(INSERT/UPDATE/DELETE 自动算派生价+刷聚合)
|
||||
string _xhTblReal = LookupTableByIndex("箱号", tuhao, "", conn);
|
||||
if (!string.IsNullOrEmpty(_xhTblReal))
|
||||
{
|
||||
using (var fakConn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(
|
||||
System.IO.Path.Combine(System.IO.Path.GetDirectoryName(GetQuoteDbPath(projectId)), "fak.db"))))
|
||||
{
|
||||
fakConn.Open();
|
||||
计算引擎.EnsureFormulaTable(fakConn);
|
||||
计算引擎.建元件表触发器(conn, tx, tblName, _xhTblReal, xhId, tuhao, TuhaoTableName,
|
||||
计算引擎.加载公式(fakConn, "元件"), 计算引擎.加载公式(fakConn, "箱号"),
|
||||
计算引擎.加载公式(fakConn, "图号"));
|
||||
}
|
||||
}'''),
|
||||
], "菜单与库.cs")
|
||||
|
||||
# ④⑤⑥ 公式重算.cs + 主窗口.cs
|
||||
p2 = ROOT + r"\主窗口.公式重算.cs"
|
||||
edit(p2, [
|
||||
# RecalcAllAfterReload 尾部落库退役(直写库架构:内存只读,不回写)
|
||||
(''' SaveYuanjianTableIfDirty();
|
||||
SaveXianghaoTableIfDirty();
|
||||
SaveTuhaoTableIfDirty();
|
||||
if (_yuanjianTable != null) _yuanjianTable.AcceptChanges();''',
|
||||
''' // ★ 直写库架构:派生值由数据库触发器算好并经"读回"刷新缓存,内存不再回写库
|
||||
if (_yuanjianTable != null) _yuanjianTable.AcceptChanges();'''),
|
||||
], "公式重算.cs")
|
||||
|
||||
p3 = ROOT + r"\主窗口.cs"
|
||||
edit(p3, [
|
||||
# 选型页回报价页:触发器已算好聚合,去掉全量重算
|
||||
(''' // ★ 步骤①:先调 RecalcAllFromDatabase 重算数据库里的箱号/图号/项目汇总
|
||||
// (此时数据库里的元件派生列已是新值,Recalc 会按新元件数据聚合)
|
||||
// 必须先重算再 Reload,否则 Reload 把旧聚合值读到内存,显示还是旧的
|
||||
RecalcAllFromDatabase(_currentProjectId);''',
|
||||
''' // ★ 直写库+触发器:元件写库时聚合已自动更新,这里只补跨库项目汇总
|
||||
计算引擎.刷新项目汇总(GetQuoteDbPath(_currentProjectId), _currentProjectId, TuhaoTableName);'''),
|
||||
# SaveAllQuoteTablesIfDirty 置空(保留 EndEdit——把 UI 编辑提交进直写队列)
|
||||
(''' private void SaveAllQuoteTablesIfDirty(bool force = true)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_currentProjectId)) return;
|
||||
try
|
||||
{
|
||||
// 先提交所有正在编辑的单元格,否则最后一笔编辑没进 DataTable
|
||||
if (this.advancedDataGridView5 != null) this.advancedDataGridView5.EndEdit();
|
||||
if (this.advancedDataGridView4 != null) this.advancedDataGridView4.EndEdit();
|
||||
if (this.advancedDataGridView9 != null) this.advancedDataGridView9.EndEdit();''',
|
||||
''' private void SaveAllQuoteTablesIfDirty(bool force = true)
|
||||
{
|
||||
// ★ 直写库架构退役:编辑已实时 UPDATE 库(触发器自动计算),无"未保存"数据。
|
||||
// 仅保留 EndEdit——把正在编辑的单元格提交进 CellValuePushed(排队直写库)。
|
||||
if (string.IsNullOrWhiteSpace(_currentProjectId)) return;
|
||||
try
|
||||
{
|
||||
if (this.advancedDataGridView5 != null) this.advancedDataGridView5.EndEdit();
|
||||
if (this.advancedDataGridView4 != null) this.advancedDataGridView4.EndEdit();
|
||||
if (this.advancedDataGridView9 != null) this.advancedDataGridView9.EndEdit();
|
||||
return;'''),
|
||||
# SaveAllToDatabaseSilent 主体置空(实时架构无保存动作;保留项目汇总刷新+项目列表刷新)
|
||||
(''' private void SaveAllToDatabaseSilent()
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(_currentProjectId)) return;
|
||||
|
||||
// 先提交所有正在编辑的单元格到内存 DataTable
|
||||
if (this.advancedDataGridView5 != null) this.advancedDataGridView5.EndEdit();
|
||||
if (this.advancedDataGridView4 != null) this.advancedDataGridView4.EndEdit();
|
||||
if (this.advancedDataGridView9 != null) this.advancedDataGridView9.EndEdit();''',
|
||||
''' private void SaveAllToDatabaseSilent()
|
||||
{
|
||||
// ★ 直写库架构退役:数据已实时在库(触发器自动计算),保存动作不复存在。
|
||||
// 只提交编辑队列 + 刷新跨库项目汇总(供项目列表页显示)。
|
||||
if (string.IsNullOrWhiteSpace(_currentProjectId)) return;
|
||||
if (this.advancedDataGridView5 != null) this.advancedDataGridView5.EndEdit();
|
||||
if (this.advancedDataGridView4 != null) this.advancedDataGridView4.EndEdit();
|
||||
if (this.advancedDataGridView9 != null) this.advancedDataGridView9.EndEdit();
|
||||
try
|
||||
{
|
||||
计算引擎.刷新项目汇总(GetQuoteDbPath(_currentProjectId), _currentProjectId, TuhaoTableName);
|
||||
}
|
||||
catch (Exception exSummary) { Log("刷新项目汇总异常: " + exSummary.Message); }
|
||||
return;
|
||||
|
||||
if (this.advancedDataGridView5 != null) this.advancedDataGridView5.EndEdit();
|
||||
if (this.advancedDataGridView4 != null) this.advancedDataGridView4.EndEdit();
|
||||
if (this.advancedDataGridView9 != null) this.advancedDataGridView9.EndEdit();'''),
|
||||
], "主窗口.cs")
|
||||
|
||||
print("合计", n, "处")
|
||||
@@ -0,0 +1,152 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""第三批接线:①CreateXianghaoTable/CreateYuanjianTable 尾部重建全部触发器(兜底所有建表路径)
|
||||
②EnterProjectCore 进项目时公式触发器自检 ③红绿"未保存"退役 ④保存按钮改实时保存提示
|
||||
⑤撤销删除(项目库)后重建触发器 ⑥EnsureFormulaTriggers/保存提示 方法体"""
|
||||
import io
|
||||
|
||||
ROOT = r"C:\Users\099978\Documents\SharpDevelop Projects\成套报价软件\成套报价软件"
|
||||
n = 0
|
||||
|
||||
def edit(path, pairs, tag):
|
||||
global n
|
||||
s = io.open(path, encoding="utf-8-sig").read()
|
||||
for old, new in pairs:
|
||||
if old not in s:
|
||||
if new in s:
|
||||
print(tag, "已有(跳过):", old[:40].replace("\n", " "))
|
||||
continue
|
||||
raise AssertionError(tag + " 锚点缺失: " + old[:80])
|
||||
s = s.replace(old, new, 1)
|
||||
n += 1
|
||||
io.open(path, "w", encoding="utf-8-sig").write(s)
|
||||
print(tag, "ok")
|
||||
|
||||
# ① 两个建表函数尾部:重建全部触发器(幂等兜底,覆盖一切建表路径)
|
||||
p1 = ROOT + r"\主窗口.菜单与库.cs"
|
||||
edit(p1, [
|
||||
(''' using (var cmd = new System.Data.SQLite.SQLiteCommand(sql, conn))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>按真实ID表名建一张空元件表。</summary>''',
|
||||
''' using (var cmd = new System.Data.SQLite.SQLiteCommand(sql, conn))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
// ★ 公式触发器兜底:重建项目库全部触发器(幂等,新表即刻具备自动计算能力)
|
||||
EnsureFormulaTriggersQuiet(projectId);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
/// <summary>按真实ID表名建一张空元件表。</summary>'''),
|
||||
(''' using (var cmd = new System.Data.SQLite.SQLiteCommand(sql, conn))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void DeleteXianghao(string projectId, string tuhao, string xianghao)''',
|
||||
''' using (var cmd = new System.Data.SQLite.SQLiteCommand(sql, conn))
|
||||
{
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
// ★ 公式触发器兜底:重建项目库全部触发器(幂等)
|
||||
EnsureFormulaTriggersQuiet(projectId);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private void DeleteXianghao(string projectId, string tuhao, string xianghao)'''),
|
||||
# ⑤ 撤销删除(项目库快照还原)后重建触发器(快照库里的触发器可能是旧公式)
|
||||
(''' // 快照 → 目标库
|
||||
BackupSQLiteDb(snapPath, _undoTargetDbPath);''',
|
||||
''' // 快照 → 目标库
|
||||
BackupSQLiteDb(snapPath, _undoTargetDbPath);
|
||||
// ★ 快照库的触发器可能是旧公式版本,还原后按当前公式表重建
|
||||
if (!wasFak) EnsureFormulaTriggersQuiet(_undoProjectId);'''),
|
||||
], "菜单与库.cs")
|
||||
|
||||
# ②⑥ 编辑直写库.cs:EnsureFormulaTriggers + 保存提示方法
|
||||
p2 = ROOT + r"\编辑直写库.cs"
|
||||
edit(p2, [
|
||||
(''' /// <summary>公式表缓存(管线建触发器时用;图号粘贴管线开头加载)。</summary>
|
||||
private List<公式条目> _公式_元件, _公式_箱号, _公式_图号;''',
|
||||
''' /// <summary>公式表缓存(管线建触发器时用;图号粘贴管线开头加载)。</summary>
|
||||
private List<公式条目> _公式_元件, _公式_箱号, _公式_图号;
|
||||
|
||||
/// <summary>进项目/建表/撤销删除后:按公式表重建项目库全部触发器(幂等,安静模式只记日志)。</summary>
|
||||
private void EnsureFormulaTriggersQuiet(string projectId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(projectId)) return;
|
||||
string dbPath = GetQuoteDbPath(projectId);
|
||||
if (!System.IO.File.Exists(dbPath)) return;
|
||||
string fak = System.IO.Path.Combine(
|
||||
System.IO.Path.GetDirectoryName(dbPath), "fak.db");
|
||||
using (var conn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(dbPath)))
|
||||
using (var fakConn = new System.Data.SQLite.SQLiteConnection(GetConnStringStatic(fak)))
|
||||
{
|
||||
conn.Open();
|
||||
fakConn.Open();
|
||||
计算引擎.EnsureFormulaTable(fakConn);
|
||||
计算引擎.重建项目全部触发器(conn, null, TuhaoTableName, fakConn);
|
||||
}
|
||||
Log("[公式触发器] 已按公式表重建 [" + projectId + "] 全部触发器");
|
||||
}
|
||||
catch (Exception ex) { Log("[公式触发器] 重建失败(不影响编辑,但派生列不会自动算): " + ex.Message); }
|
||||
}
|
||||
|
||||
/// <summary>保存按钮(功能区"保存"):实时架构下数据已在库,只刷跨库项目汇总并提示。</summary>
|
||||
private void 保存按钮_实时保存提示()
|
||||
{
|
||||
try { 计算引擎.刷新项目汇总(GetQuoteDbPath(_currentProjectId), _currentProjectId, TuhaoTableName); }
|
||||
catch (Exception ex) { Log("[保存] 刷新项目汇总异常: " + ex.Message); }
|
||||
SetStatus("数据已实时保存在数据库");
|
||||
MessageBox.Show(this, "数据已实时保存在数据库:\\n编辑即写库,价格由数据库触发器自动计算,无需手动保存。",
|
||||
"保存", MessageBoxButtons.OK, MessageBoxIcon.Information);
|
||||
}'''),
|
||||
], "编辑直写库.cs")
|
||||
|
||||
# ②③④ 主窗口.cs + 公式重算.cs
|
||||
p3 = ROOT + r"\主窗口.cs"
|
||||
edit(p3, [
|
||||
# EnterProjectCore:进项目自检(幂等重建)
|
||||
(''' private void EnterProjectCore(string pid)
|
||||
{
|
||||
Log("[EnterProjectCore] 开始进入项目 [" + pid + "], _isTabSyncing=" + _isTabSyncing);''',
|
||||
''' private void EnterProjectCore(string pid)
|
||||
{
|
||||
Log("[EnterProjectCore] 开始进入项目 [" + pid + "], _isTabSyncing=" + _isTabSyncing);
|
||||
// ★ 公式触发器自检:按公式表重建该项目库全部触发器(幂等;老库补装/公式变更生效)
|
||||
EnsureFormulaTriggersQuiet(pid);'''),
|
||||
], "主窗口.cs")
|
||||
|
||||
p4 = ROOT + r"\主窗口.公式重算.cs"
|
||||
edit(p4, [
|
||||
# 红绿"未保存"退役(实时架构库里永远最新)
|
||||
(''' dgv.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
标记未保存(); // 状态栏:保存状态置"未保存"''',
|
||||
''' dgv.CommitEdit(DataGridViewDataErrorContexts.Commit);
|
||||
// ★ 直写库架构:编辑即写库,不再有"未保存"状态'''),
|
||||
], "公式重算.cs")
|
||||
|
||||
p5 = ROOT + r"\MainForm.cs"
|
||||
edit(p5, [
|
||||
# 保存按钮 → 实时保存提示
|
||||
(''' case "button17": SaveAllToDatabase(); break; // 保存''',
|
||||
''' case "button17": 保存按钮_实时保存提示(); break; // 保存(实时架构:提示+刷项目汇总)'''),
|
||||
], "MainForm.cs")
|
||||
|
||||
print("合计", n, "处")
|
||||
@@ -0,0 +1,61 @@
|
||||
// 直写批复现:用与 直写Timer 完全相同的连接串/事务/UPDATE 序列,复现 SQL logic error
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
class 复现直写
|
||||
{
|
||||
[STAThread]
|
||||
static void Main()
|
||||
{
|
||||
string dbPath = @"C:\成套报价软件\sjk\bjb\项目20260908001.db";
|
||||
string cs = "Data Source=" + dbPath + ";Version=3;Journal Mode=WAL;Synchronous=Normal;Pooling=True;";
|
||||
var 批 = new List<string[]>
|
||||
{
|
||||
new[]{"yj_1","1","报出系数","1.5"},
|
||||
new[]{"yj_1","1","采购单价","1078.875"},
|
||||
new[]{"yj_1","1","采购价","1078.875"},
|
||||
new[]{"yj_1","1","报出单价","1078.875"},
|
||||
new[]{"yj_1","1","报出价","1078.875"},
|
||||
new[]{"xh_1","1","单台成本","1078.875"},
|
||||
new[]{"xh_1","1","成本价","1078.875"},
|
||||
new[]{"xh_1","1","单台报出","1078.875"},
|
||||
new[]{"xh_1","1","报出价","1078.875"},
|
||||
new[]{"xh_1","1","利润","0"},
|
||||
new[]{"xh_1","1","利润率","0"},
|
||||
new[]{"xh_1","1","毛利率","0"},
|
||||
new[]{"advancedDataGridView9Table","1","箱柜数量","2"},
|
||||
new[]{"advancedDataGridView9Table","1","单价","7552.13"},
|
||||
new[]{"advancedDataGridView9Table","1","总价","7552.13"},
|
||||
new[]{"advancedDataGridView9Table","1","成本总价","7552.13"},
|
||||
new[]{"advancedDataGridView9Table","1","毛利","0"},
|
||||
new[]{"advancedDataGridView9Table","1","毛利率","0"},
|
||||
};
|
||||
try
|
||||
{
|
||||
using (var conn = new System.Data.SQLite.SQLiteConnection(cs))
|
||||
{
|
||||
conn.Open();
|
||||
using (var tx = conn.BeginTransaction())
|
||||
{
|
||||
foreach (var item in 批)
|
||||
{
|
||||
using (var cmd = new System.Data.SQLite.SQLiteCommand(
|
||||
"UPDATE \"" + item[0].Replace("\"", "") + "\" SET \"" + item[2].Replace("\"", "") +
|
||||
"\"=@v WHERE rowid=@r;", conn, tx))
|
||||
{
|
||||
cmd.Parameters.AddWithValue("@v", item[3]);
|
||||
cmd.Parameters.AddWithValue("@r", long.Parse(item[1]));
|
||||
cmd.ExecuteNonQuery();
|
||||
}
|
||||
}
|
||||
tx.Commit();
|
||||
}
|
||||
}
|
||||
Console.WriteLine("全部 OK —— 未复现");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("复现: " + ex.GetType().Name + " " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
109
|
||||
@@ -0,0 +1,71 @@
|
||||
# ============================================================
|
||||
# 扒图同步检查.ps1 —— Cad/Pdf 扒图窗口 Form3.cs 漂移护栏
|
||||
# ============================================================
|
||||
# 背景: 扒图\Cad\Form3.cs 与 扒图\Pdf\Form3.cs 是约 98% 重复的双胞胎文件
|
||||
# (物理合并被 SD 设计器"项目内自定义类型"死结阻挡,2026-10-01 决策维持双文件)。
|
||||
# 历史上 Pdf 侧修复漏同步回 Cad(AdgvMsgHook 右键多选保持 / F1 合并防抖),
|
||||
# 本脚本防止再次漂移。
|
||||
# 原理: 两文件做字面映射归一化(CadBridge→PdfBridge / cadbt→pdfbt / CAD→PDF /
|
||||
# namespace MyApp→MyAppPdf)后 git diff,差异行数不超过基线即 PASS。
|
||||
# 用法: powershell -ExecutionPolicy Bypass -File tools\扒图同步检查.ps1
|
||||
# 加 -UpdateBaseline 把当前差异量定为新基线(确认属合法演进后使用)。
|
||||
# ============================================================
|
||||
param([switch]$UpdateBaseline)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$root = Split-Path -Parent $PSScriptRoot
|
||||
$cadPath = Join-Path $root '成套报价软件\扒图\Cad\Form3.cs'
|
||||
$pdfPath = Join-Path $root '成套报价软件\扒图\Pdf\Form3.cs'
|
||||
$baseline = Join-Path $PSScriptRoot '扒图同步基线.txt'
|
||||
|
||||
foreach ($f in @($cadPath, $pdfPath)) {
|
||||
if (-not (Test-Path $f)) { Write-Host "[FAIL] 缺少文件: $f" -ForegroundColor Red; exit 2 }
|
||||
}
|
||||
|
||||
function Normalize([string]$path) {
|
||||
(Get-Content $path -Encoding UTF8) | ForEach-Object {
|
||||
$_ = $_ -creplace '^namespace MyApp\s*$', 'namespace MyAppPdf'
|
||||
$_ = $_ -creplace 'CadBridge', 'PdfBridge'
|
||||
$_ = $_ -creplace 'cadbt', 'pdfbt'
|
||||
$_ = $_ -creplace 'CAD', 'PDF'
|
||||
$_
|
||||
}
|
||||
}
|
||||
|
||||
$tmpCad = Join-Path $env:TEMP 'baotu_cad_norm.cs'
|
||||
$tmpPdf = Join-Path $env:TEMP 'baotu_pdf_norm.cs'
|
||||
Normalize $cadPath | Set-Content $tmpCad -Encoding UTF8
|
||||
Normalize $pdfPath | Set-Content $tmpPdf -Encoding UTF8
|
||||
|
||||
$diffFile = Join-Path $env:TEMP 'baotu_drift.diff'
|
||||
$null = git diff --no-index --unified=1 $tmpCad $tmpPdf 2>$null | Out-File $diffFile -Encoding UTF8
|
||||
|
||||
$numstat = git diff --no-index --numstat $tmpCad $tmpPdf 2>$null
|
||||
$added = 0; $deleted = 0
|
||||
if ($numstat) {
|
||||
$parts = (@($numstat)[0]) -split "`t"
|
||||
if ($parts.Count -ge 2) { $added = [int]$parts[0]; $deleted = [int]$parts[1] }
|
||||
}
|
||||
$drift = $added + $deleted
|
||||
|
||||
if ($UpdateBaseline) {
|
||||
Set-Content $baseline -Value $drift -Encoding ASCII
|
||||
Write-Host "[OK] 基线已更新为 $drift 行差异 (完整差异: $diffFile)"
|
||||
exit 0
|
||||
}
|
||||
|
||||
if (-not (Test-Path $baseline)) {
|
||||
Write-Host "[FAIL] 缺少基线文件 $baseline ,请先跑一次 -UpdateBaseline 生成" -ForegroundColor Red
|
||||
exit 2
|
||||
}
|
||||
$limit = [int](Get-Content $baseline -TotalCount 1)
|
||||
|
||||
Write-Host ("扒图 Cad/Pdf 归一化差异: {0} 行 (基线上限 {1} 行)" -f $drift, $limit)
|
||||
if ($drift -gt $limit) {
|
||||
Write-Host "[FAIL] 差异超过基线 —— 两文件已漂移!请排查:" -ForegroundColor Red
|
||||
Write-Host " ① 是否一侧修了 bug 忘了同步另一侧?(最常见原因)"
|
||||
Write-Host " ② 完整差异见: $diffFile"
|
||||
Write-Host " ③ 确属合法演进后,用 -UpdateBaseline 刷新基线"
|
||||
exit 1
|
||||
}
|
||||
Write-Host "[PASS] 同步状态正常"
|
||||
Reference in New Issue
Block a user