[{"data":1,"prerenderedAt":410},["ShallowReactive",2],{"project-wails-bailer-starter":3,"all-projects-nav":317,"project-posts-wails-bailer-starter":409},{"id":4,"title":5,"architecture":6,"body":7,"category":282,"client":283,"cover":284,"date":285,"demo":286,"demoMobile":286,"description":273,"extension":287,"featured":288,"features":289,"github":297,"manual":286,"meta":298,"modules":286,"navigation":299,"order":300,"path":301,"seo":302,"slug":303,"stem":304,"summary":305,"techStack":306,"visible":299,"year":315,"__hash__":316},"projects\u002Fprojects\u002Fwails-bailer-starter.md","Wails 桌面端脚手架基座","https:\u002F\u002Fname.os.bennywen.xyz\u002Fbennywen.xyz\u002Fassets\u002Fwails-bailer-starter\u002Fwails-bailer-architecture.svg",{"type":8,"value":9,"toc":272},"minimark",[10,14,18,21,24,27,35,140,143,210,220,223,242,245],[11,12,13],"h2",{"id":13},"缘起",[15,16,17],"p",{},"第一个桌面客户端发出去，收到的反馈是「打不开」，而我这边跑得好好的。想定位就得知道它崩在哪一步，可应用连一行日志都没往盘上落。那之后我给自己定了条规矩：任何桌面项目，先让它在别人的机器上说得清自己死在哪。",[11,19,20],{"id":20},"背景",[15,22,23],{},"桌面客户端项目每次起盘，操作系统这一层都要重新打交道：窗口生命周期、单实例锁、系统托盘、日志轮转、配置读写、崩溃捕获、\n打包安装。这些与业务无关，但每一件都要做一次决定，而每次决定都可能和上一个项目不一致。需要一套与业务\n无关、可反复复用的桌面基座，把这些\"管道\"沉淀下来，作为后续所有桌面项目的起点。",[11,25,26],{"id":26},"方案",[15,28,29,30,34],{},"基座的定位是「",[31,32,33],"strong",{},"只给管道，产品面下游自己长","」——它提供运行时能力与契约，不预置业务页面：",[36,37,38,50,96,113,127],"ul",{},[39,40,41,44,45,49],"li",{},[31,42,43],{},"运行时能力","：单实例锁（二次启动唤起已有窗口）、系统托盘 + 右键菜单（Windows\u002FLinux）、\n日志轮转（5MB × 3 份）、",[46,47,48],"code",{},"config.json"," 读写、原生对话框、panic 落盘、优雅关闭",[39,51,52,55,56],{},[31,53,54],{},"管道契约","（Go 与 TS 两端单一真相在 Go）：\n",[36,57,58,72,86],{},[39,59,60,61,64,65,68,69],{},"错误协议 ",[46,62,63],{},"internal\u002Fapperr"," + Wails ",[46,66,67],{},"ErrorFormatter"," → 前端拿到 ",[46,70,71],{},"{code, message}",[39,73,74,75,78,79,82,83],{},"分页协议 ",[46,76,77],{},"internal\u002Fpage"," 的 ",[46,80,81],{},"Request"," \u002F ",[46,84,85],{},"Result[T]",[39,87,88,89,78,92,95],{},"事件协议 ",[46,90,91],{},"internal\u002Fevent",[46,93,94],{},"\u003Cdomain>:\u003Caction>"," 命名约定",[39,97,98,101,102,105,106,82,109,112],{},[31,99,100],{},"架构护栏","：把约定编译成\"会红的检查\"——分层越界、模型注册双向校验、依赖登记制\n（",[46,103,104],{},"deps.yaml"," 双向校验）、前端 import 安全与硬编码色值，随 ",[46,107,108],{},"make test",[46,110,111],{},"make lint"," 执行；\n护栏\"感知自己瞎了\"：解析到 0 个结果会 Fatal 而非静默放行",[39,114,115,118,119,122,123,126],{},[31,116,117],{},"模块生成器","：",[46,120,121],{},"make gen name=\u003C模块>"," 从黄金范例 ",[46,124,125],{},"_example\u002F"," 生成 model + service + 绑定 +\n页面 + 路由 + 菜单，锚点注入且幂等",[39,128,129,118,132,135,136,139],{},[31,130,131],{},"打包发布",[46,133,134],{},"make package"," 产出 macOS 美化 dmg（Applications 软链 + 背景图合成 + 箭头\n同源对齐 + 回读校验）或 Windows NSIS 安装器（",[46,137,138],{},"scope=user|machine","）",[11,141,142],{"id":142},"技术架构",[144,145,146,159],"table",{},[147,148,149],"thead",{},[150,151,152,156],"tr",{},[153,154,155],"th",{},"模块",[153,157,158],{},"技术选型",[160,161,162,171,179,191,202],"tbody",{},[150,163,164,168],{},[165,166,167],"td",{},"后端",[165,169,170],{},"Go ≥ 1.25 + gorm + SQLite（纯 Go 驱动，交叉编译无 CGO）",[150,172,173,176],{},[165,174,175],{},"桌面运行时",[165,177,178],{},"Wails v2（WebView2 \u002F WKWebView）",[150,180,181,184],{},[165,182,183],{},"前端",[165,185,186,187,190],{},"Vue3 + Vite + Element Plus + Vue Router（hash 模式适配 ",[46,188,189],{},"file:\u002F\u002F","）+ Pinia",[150,192,193,196],{},[165,194,195],{},"设计系统",[165,197,198,199,139],{},"暗色优先，单主色派生全色阶（",[46,200,201],{},"tokens.css",[150,203,204,207],{},[165,205,206],{},"工程化",[165,208,209],{},"Makefile 统一入口 + 架构护栏（AST \u002F ESLint）+ 依赖登记制 + OpenSpec 治理",[15,211,212,213,216,217,219],{},"采用五层分层（L1 桌面运行时 → L2 前端渲染 → L3 绑定方法 ",[46,214,215],{},"app.go"," → L4 业务服务 →\nL5 数据存储），",[46,218,215],{}," 不直接 import gorm\u002Fdatabase，业务一律经 service，由护栏强制约束。",[11,221,222],{"id":222},"成果",[15,224,225,226,229,230,233,234,237,238,241],{},"形成了可反复复用的桌面客户端母版：",[46,227,228],{},"scripts\u002Finit.sh myapp"," 一键实例化（替换模块名占位符\n",[46,231,232],{},"__APP_NAME__"," 并清空归档历史），",[46,235,236],{},"make gen"," 生成业务模块骨架，",[46,239,240],{},"make test\u002Flint\u002Fsmoke"," 靠护栏\n与冒烟证明\"没改坏\"。macOS 产出带拖拽引导的美化 dmg，Windows 产出免管理员的 NSIS 安装器，\n目标机器无需 Go \u002F Node.js 运行时。",[11,243,244],{"id":244},"已知限制",[36,246,247,253,259],{},[39,248,249,252],{},[31,250,251],{},"macOS 无系统托盘","：Wails v2 的 NSApplication delegate 与所有 systray 库冲突，故 macOS\n关闭即退出，托盘能力仅在 Windows\u002FLinux 提供",[39,254,255,258],{},[31,256,257],{},"无 headless 模式","：冒烟测试定位为本地验证，CI 只编译不启动 GUI",[39,260,261,264,265,267,268,271],{},[31,262,263],{},"母版防呆","：含 ",[46,266,232],{}," 占位符时拒绝打包，需先执行 ",[46,269,270],{},"scripts\u002Finit.sh"," 实例化",{"title":273,"searchDepth":274,"depth":274,"links":275},"",2,[276,277,278,279,280,281],{"id":13,"depth":274,"text":13},{"id":20,"depth":274,"text":20},{"id":26,"depth":274,"text":26},{"id":142,"depth":274,"text":142},{"id":222,"depth":274,"text":222},{"id":244,"depth":274,"text":244},"toolkit","开源项目","https:\u002F\u002Fname.os.bennywen.xyz\u002Fbennywen.xyz\u002Fassets\u002Fwails-bailer-starter\u002Fwails-bailer-dashboard.webp","2026-09-15",null,"md",false,[290,293],{"title":291,"description":292,"image":284},"仪表盘 · 应用壳","无边框标题栏（分平台）+ 侧栏菜单 + 内容区，开箱即用的 B 端布局",{"title":294,"description":295,"image":296},"设置页 · 主题切换","暗色优先的设计令牌，改主色即可派生全色阶；配置本地持久化","https:\u002F\u002Fname.os.bennywen.xyz\u002Fbennywen.xyz\u002Fassets\u002Fwails-bailer-starter\u002Fwails-bailer-settings.webp","https:\u002F\u002Fgithub.com\u002FBenny66\u002Fwails-bailer-starter",{},true,15,"\u002Fprojects\u002Fwails-bailer-starter",{"title":5,"description":273},"wails-bailer-starter","projects\u002Fwails-bailer-starter","基于 Wails v2 的桌面客户端脚手架基座——Go + Vue3，内置运行时能力、管道契约与架构护栏",[307,308,309,310,311,312,313,314],"Go","GORM","SQLite","Wails","Vue3","Element Plus","Vite","Pinia","2026","Zf-GkRJVdDKbLxi1w2yCDTWppEaRbnDqew1MTmue77Y",[318,324,329,335,340,345,346,351,356,359,364,370,376,380,385,389,393,397,401,405],{"title":319,"slug":320,"order":321,"year":315,"date":322,"category":323},"图片工坊 · 离线批量图片处理","image-workshop",20,"2026-10-24","desktop",{"title":325,"slug":326,"order":327,"year":315,"date":328,"category":323},"Morrow · 桌面健康提醒","morrow",19,"2026-10-23",{"title":330,"slug":331,"order":332,"year":315,"date":333,"category":334},"家教学情管理系统","tutor-analytics",17,"2026-09-30","operations",{"title":336,"slug":337,"order":338,"year":315,"date":333,"category":339},"闲鱼素材工坊","xianyu-content-gen",18,"ai",{"title":341,"slug":342,"order":343,"year":315,"date":344,"category":339},"电商详情页 AI 生成客户端","ecom-detail-ai",16,"2026-09-29",{"title":5,"slug":303,"order":300,"year":315,"date":285,"category":282},{"title":347,"slug":348,"order":349,"year":315,"date":350,"category":282},"多端管理脚手架基座","admin-scaffold",8,"2026-09-04",{"title":352,"slug":353,"order":354,"year":315,"date":355,"category":334},"公车车辆管理系统","bus-admin-system",1,"2026-09-02",{"title":357,"slug":358,"order":274,"year":315,"date":355,"category":334},"设备出租订单管理系统","rental-order-manager",{"title":360,"slug":361,"order":362,"year":315,"date":363,"category":339},"家庭绿植管家","family-plant-manager",3,"2026-08-19",{"title":365,"slug":366,"order":367,"year":368,"date":369,"category":339},"古诗词AI取名工具","chinese-poetry-give-name",7,"2025","2026-06-22",{"title":371,"slug":372,"order":373,"year":368,"date":374,"category":375},"企业固定资产管理系统","asset-admin",4,"2026-06-16","admin",{"title":377,"slug":378,"order":379,"year":368,"date":374,"category":339},"知祖AI","kingpt",5,{"title":381,"slug":382,"order":383,"year":368,"date":374,"category":384},"深圳房产智能分析平台","sz-realestate",6,"data",{"title":386,"slug":387,"order":388,"year":368,"date":374,"category":375},"工厂轻量化智能排班系统","staff-scheduling",9,{"title":390,"slug":391,"order":392,"year":368,"date":374,"category":334},"电商服装仓库物流管理系统","clothing-warehouse",10,{"title":394,"slug":395,"order":396,"year":368,"date":374,"category":339},"知识库问答系统","kb-qa",11,{"title":398,"slug":399,"order":400,"year":368,"date":374,"category":375},"企业微信邮箱运营管理系统","mail-manager",12,{"title":402,"slug":403,"order":404,"year":368,"date":374,"category":334},"Switch 电玩库存管理系统","switch-manager",13,{"title":406,"slug":407,"order":408,"year":368,"date":374,"category":375},"学生宿舍管理系统","dorm-manager",14,[],1792857942269]