# -*- 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 网页引擎运行时;2026-10-01 独立 cadbt.exe 退役,只装 node+CAD-Viewer) ===== cadbt_src = os.path.join(ROOT, "cadbt", "cadbt", "bin", "Debug") cadbt_dst = os.path.join(REL, "cadbt") 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 网页服务起不来!)") print("复制目录 cadbt(node+CAD-Viewer 网页引擎+fonts/templates/server.cjs;独立 exe 已退役)") # ===== 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()