]> acesimba.cloud Git - StationBuilder.git/commitdiff
docs: 新增设计文档 design-doc.html 并增加顶栏「设计方案」入口
authorwang.yu <wang.yu@advantech.com>
Mon, 17 Aug 2026 08:51:49 +0000 (16:51 +0800)
committerwang.yu <wang.yu@advantech.com>
Mon, 17 Aug 2026 08:51:49 +0000 (16:51 +0800)
- public/design-doc.html: 朴实风格的在线设计文档,逐页/逐元素说明功能、数据来源、交互效果,含端到端生成流程、AI 问答、模态框、数据存储,未实现功能标注「尚未实现」,支持导出 PDF/Word/Markdown
- app/page.tsx: 顶栏登录名左侧新增「设计方案」超链,新窗口打开 design-doc.html
- app/globals.css: 新增 .design-link 样式

app/globals.css
app/page.tsx
public/design-doc.html [new file with mode: 0644]

index 63cb841091668857f30617b125535a457154773a..58e3d0f2165dfef1e2b97d2cda202e6868d865fc 100644 (file)
@@ -16,7 +16,9 @@
 .model-row.disabled{opacity:.45;cursor:not-allowed;background:#f4f4f4}
 .model-row.disabled .model-tag{color:#9aa1a5;background:#eceeef;border-color:#dfe2e3}
 .model-row:not(.disabled):hover{border-color:#8ccce9;background:#eef7fb}.workspace{min-height:calc(100vh - 42px);padding-bottom:64px}.crumb{height:34px;padding:11px 20px 0;color:#8b8f93;font-size:11px}.crumb b{color:var(--blue)}.page-heading{min-height:60px;padding:3px 20px 11px;display:flex;align-items:center;justify-content:space-between}.page-heading h1{font-size:21px;margin:0 0 5px}.page-heading p{margin:0;color:#81868b}.button{border:1px solid var(--blue2);background:var(--blue2);color:white;border-radius:2px;min-width:92px;height:31px;padding:0 14px;margin-left:8px}.button:hover{background:#006ca9}.button.secondary{background:white;color:#555;border-color:#cdd2d6}.button.secondary:hover{border-color:var(--blue);color:var(--blue)}.button:disabled{opacity:.55;cursor:not-allowed}.steps{height:70px;background:#fff;border-top:1px solid var(--line);border-bottom:1px solid var(--line);display:grid;grid-template-columns:repeat(4,1fr);padding:0 22px}.steps button{border:0;background:transparent;display:flex;align-items:center;position:relative;text-align:left;color:#999;padding:0 18px}.steps button>i{font-style:normal;width:29px;height:29px;border-radius:50%;border:1px solid #cfd4d9;display:grid;place-items:center;margin-right:10px;background:#fff}.steps button span{display:flex;flex-direction:column;gap:4px}.steps button small{font-size:8px}.steps button>b{position:absolute;right:0;font-size:23px;color:#c3c7ca}.steps button.active{color:#222}.steps button.active>i{background:var(--blue);border-color:var(--blue);color:#fff}.steps button.done>i{background:#39b980;border-color:#39b980;color:white}.content{padding:14px 20px}.panel{background:white;border:1px solid var(--line);margin-bottom:14px}.panel-head{min-height:43px;border-bottom:1px solid var(--line);padding:8px 12px;display:flex;justify-content:space-between;align-items:center}.panel-head>strong{font-size:14px}.panel-head .button{height:28px}.panel-body{padding:12px}.summary-strip{display:grid;grid-template-columns:repeat(5,1fr);height:64px;background:white;border:1px solid var(--line);margin-bottom:14px}.summary-strip div{display:flex;align-items:center;justify-content:center;gap:9px;border-right:1px solid var(--line)}.summary-strip div:last-child{border:0}.summary-strip b{font-size:23px}.summary-strip span{color:#777}.filterbar{height:45px;display:flex;align-items:center;gap:7px;padding-bottom:10px;border-bottom:1px solid var(--line)}.filterbar>label{width:205px;height:30px;border:1px solid #cdd2d6;display:flex;align-items:center;padding:0 8px;color:#999}.filterbar input{border:0;outline:0;width:100%;padding-left:8px}.filterbar select,.preview-toolbar select{height:30px;border:1px solid #cdd2d6;background:white;padding:0 25px 0 9px}.filterbar>span{flex:1}.view-switch{display:flex}.view-switch button,.icon-button{width:31px;height:29px;border:1px solid #cbd0d4;background:white}.view-switch button.active{background:var(--blue);color:white;border-color:var(--blue)}.icon-button{border:0;font-size:16px}.data-table{width:100%;border-collapse:collapse}.data-table th{height:35px;background:#f6f7f8;color:#4b4f53;text-align:left;font-size:11px;font-weight:bold;border-bottom:1px solid var(--line);padding:0 10px}.data-table td{height:48px;border-bottom:1px solid #e3e5e7;padding:7px 10px;font-size:11px}.data-table tr:hover td{background:#f7fbfd}.link{border:0;background:transparent;color:#008ed2;padding:0}.site-icon{display:inline-grid;place-items:center;width:30px;height:30px;margin-right:8px;color:#098dd5;border:1px solid #c8d8e0;background:#f7fcff;font-size:18px;vertical-align:middle}.site-icon.big{width:40px;height:40px;font-size:26px}.status{display:inline-flex;align-items:center;justify-content:center;border-radius:10px;padding:2px 8px;font-size:10px;line-height:1.2;border:1px solid}.status.ok{color:#008c51;background:#e5fff3;border-color:#60d9a2}.status.warn{color:#b37400;background:#fff4d8;border-color:#f6bc44}.status.bad{color:#d84632;background:#ffeae7;border-color:#ff8b7b}.status.muted{color:#777;background:#f3f3f3;border-color:#c9c9c9}.dots,.trash{border:0;background:transparent;color:#777}.pagination{height:46px;display:flex;justify-content:flex-end;align-items:center;gap:7px;padding-top:12px}.pagination span{margin-right:10px;color:#777}.pagination button{width:28px;height:28px;border:0;background:white}.pagination button.active{background:#dff2fc;color:#008fd2}.pagination select{border:0;background:white}.project-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;padding:12px 0}.project-grid article{border:1px solid var(--line);padding:14px;cursor:pointer}.project-grid article:hover{border-color:var(--blue);box-shadow:0 2px 7px #008fd21f}.project-grid article>div{display:flex;justify-content:space-between}.project-grid h3{margin:10px 0 5px}.project-grid p,.project-grid small{color:#888;font-size:11px}.project-grid section{display:flex;gap:18px;border-top:1px solid #eee;border-bottom:1px solid #eee;padding:10px 0;margin:12px 0}.project-grid section span{display:flex;flex-direction:column}.project-grid section b{font-size:16px}.two-column{display:grid;grid-template-columns:1fr 340px;gap:14px}.two-column.equal{grid-template-columns:1fr 1fr}.basic-layout>.panel>.panel-body{padding:17px}.stack{display:flex;flex-direction:column}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:18px}.field{display:flex;flex-direction:column;gap:7px}.field>span{font-size:11px;font-weight:bold}.field>span i{color:red;font-style:normal;margin-right:3px}.field input,.field select,.field textarea{border:1px solid #cfd3d7;background:white;border-radius:2px;padding:8px;outline:none}.field input,.field select{height:34px}.field textarea{min-height:130px;resize:vertical;line-height:1.6}.field textarea.small{min-height:80px}.field small{color:#999;font-size:10px}.form-grid .field:nth-child(n+3){grid-column:1/-1}.upload{height:90px;border:1px dashed #bfc7cd;background:#fafbfb;display:flex;flex-direction:column;justify-content:center;align-items:center;color:#777;gap:5px}.upload b{color:var(--blue);font-size:20px}.ai-box{text-align:center;padding:12px}.ai-box>span{width:42px;height:42px;border-radius:50%;background:linear-gradient(135deg,#05a9e8,#8056d9);color:#fff;display:grid;place-items:center;margin:auto;font-weight:bold}.ai-box h3{font-size:14px;margin:12px}.ai-box p{color:#777;line-height:1.7;text-align:left}.meter{height:7px;border-radius:5px;background:#edf0f2;overflow:hidden}.meter i{display:block;height:100%;background:linear-gradient(90deg,#00a4e7,#42c58a);transition:width .35s}.tips{padding-left:22px;line-height:2;color:#666}.kpi-row{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:14px}.kpi-row>div{height:75px;border:1px solid var(--line);background:white;padding:12px 14px;position:relative}.kpi-row span{display:block;color:#008dcf;font-size:11px;margin-bottom:6px}.kpi-row b{font-size:25px}.kpi-row small{margin-left:4px;color:#888}.kpi-row .status{margin-top:7px}.editable td{padding:7px}.editable input,.editable select{height:29px;border:1px solid #d3d7db;width:100%;padding:0 7px;background:#fff}.editable .number{width:65px}.tree{line-height:2.2}.tree .l1{padding-left:24px}.tree .l2{padding-left:52px;color:#777}.tree span{border-radius:10px;background:#eef2f4;padding:2px 7px;font-size:10px}.file-card{display:flex;align-items:center;gap:12px;border:1px solid #e0e3e5;background:#fafafa;padding:12px}.file-card .excel{width:36px;height:42px;background:#14834f;color:white;display:grid;place-items:center;font-weight:bold}.file-card>div{flex:1}.file-card p{margin:5px 0 0;color:#888;font-size:10px}.checks{display:flex;gap:20px;color:#15935c;padding:12px 2px 0;font-size:10px}.tabs{height:46px;border-bottom:1px solid var(--line);display:flex;gap:6px;margin:-12px -12px 0;padding-left:10px}.tabs button{padding:0 18px;border:0;border-bottom:2px solid transparent;background:white}.tabs button.active{color:#008ed2;border-bottom-color:#008ed2}.tabs i{font-style:normal;border-radius:10px;background:#e7f3f8;padding:2px 7px;margin-left:5px}.section-toolbar{display:flex;justify-content:space-between;align-items:center;padding:14px 4px}.section-toolbar p{margin:4px 0 0;color:#888;font-size:11px}.mu-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.mu-grid article{border:1px solid var(--line);display:grid;grid-template-columns:42% 58%;min-height:128px;cursor:pointer}.mu-grid article.selected{border:2px solid var(--blue);background:#fbfeff}.mu-thumb{margin:8px;background:#e9eef1;display:grid;place-items:center;position:relative;overflow:hidden}.mu-thumb:after{content:"";width:72%;height:40%;border:1px solid #8ec4dd;background:white;box-shadow:26px -23px 0 -8px #b9e1f3,-18px 25px 0 -7px #8ed1ec}.mu-thumb b{position:absolute;z-index:2;color:var(--blue);font-size:22px}.mu-grid article>div:last-child{padding:11px 10px 8px 0}.mu-grid header{display:flex;gap:7px;align-items:center}.mu-grid h3{font-size:13px;margin:0}.mu-grid p{color:#777;font-size:10px;line-height:1.5;min-height:32px}.mu-grid button{border:0;background:transparent;color:#008ed2;padding:0}.blue{color:#008ed2}.type-tag{background:#eff3f5;padding:4px 8px;border-radius:2px}.wide-cell{min-width:300px}.menu-builder{display:grid;grid-template-columns:230px 1fr 240px;gap:14px;background:#f6f7f8;padding:14px}.page-pool,.menu-tree,.menu-preview{background:white;border:1px solid var(--line);padding:12px}.page-pool p{color:#999;font-size:10px}.page-pool div{border:1px solid var(--line);padding:10px;margin-top:8px;background:#fafafa}.menu-tree section{border:1px solid var(--line);margin-bottom:8px;padding:10px}.menu-tree section>span{float:right}.menu-tree section>div{margin:9px 0 0 15px;padding:7px;border-top:1px solid #eee}.menu-tree i{font-style:normal;font-size:8px;background:#e1f0f8;color:#008ed2;padding:3px;margin-right:8px}.menu-tree i.cube{background:#eee8fb;color:#7045bc}.menu-tree>button{border:1px dashed #aaa;background:#fafafa;width:100%;height:34px}.menu-preview aside{background:#f4f5f6;min-height:250px;display:flex;flex-direction:column;padding-top:10px}.menu-preview strong{padding:12px}.menu-preview button{border:0;background:transparent;text-align:left;padding:10px}.menu-preview button.active{background:#d8effa;color:#0089ca}.generate-layout{display:grid;grid-template-columns:1.1fr .9fr;gap:14px}.confirm-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}.confirm-grid div{border:1px solid #dfe2e5;padding:12px;display:flex;flex-direction:column;gap:5px;position:relative}.confirm-grid span,.confirm-grid small{color:#888;font-size:10px}.confirm-grid button{position:absolute;right:10px;top:10px;border:0;background:transparent;color:#008ed2;font-size:10px}.option-list{display:grid;grid-template-columns:1fr 1fr;gap:12px}.option-list label{display:flex;align-items:center;gap:8px}.task-panel{height:100%}.overall{background:#f6fbfd;border:1px solid #d3eaf5;padding:13px}.overall>div:first-child{display:flex;justify-content:space-between;margin-bottom:9px}.overall p{margin:8px 0 0;color:#777;font-size:10px}.task-list{padding:10px 0}.task-list>div{display:flex;align-items:center;border-bottom:1px solid #eee;padding:9px 3px}.task-list>div>i{font-style:normal;width:25px;height:25px;border-radius:50%;border:1px solid #d2d6da;display:grid;place-items:center;margin-right:9px}.task-list>div>i.done{background:#31b67c;border-color:#31b67c;color:white}.task-list>div>i.spin{color:#008fd2}.task-list>div>span{display:flex;flex:1;flex-direction:column;gap:3px}.task-list small{color:#999}.task-panel>.panel-body>.button{width:100%;margin:5px 0}.result-header{display:flex;align-items:center;justify-content:space-between;background:white;border:1px solid var(--line);padding:12px;margin-bottom:14px}.result-header>div{display:flex;align-items:center;gap:10px}.result-header h2{margin:0 0 4px}.result-header p{margin:0;color:#888;font-size:10px}.result-layout{display:grid;grid-template-columns:1fr 280px;gap:14px}.result-pages{display:grid;grid-template-columns:repeat(3,1fr);gap:10px}.result-pages article{border:1px solid var(--line)}.result-pages article>div:last-child{padding:10px}.result-pages article header{display:flex;justify-content:space-between}.result-pages h3{font-size:13px;margin:10px 0 4px}.result-pages p,.result-pages small{font-size:10px;color:#888}.result-pages footer{display:flex;margin-top:9px}.result-pages footer .button{margin:0 5px 0 0;min-width:68px;height:27px}.dashboard-thumb{height:115px;background:#f2f5f7;position:relative;padding:12px 10px 10px 35px}.dashboard-thumb>i{position:absolute;left:0;top:0;bottom:0;width:25px;background:#d7e2e8}.dashboard-thumb>header{display:block;height:10px;background:white;border:1px solid #d9e1e5}.dashboard-thumb>section{display:flex;gap:4px;margin-top:5px}.dashboard-thumb>section b{height:22px;flex:1;background:white;border:1px solid #cde0e9}.dashboard-thumb>footer{position:absolute;left:35px;right:46px;bottom:10px;height:50px;background:white;border:1px solid #d8e1e6;display:flex;align-items:flex-end;gap:5px;padding:8px}.dashboard-thumb>footer em{flex:1;background:#67c5ea;height:45%}.dashboard-thumb>footer em:nth-child(2){height:75%}.dashboard-thumb>footer em:nth-child(4){height:92%}.dashboard-thumb>span{position:absolute;right:9px;bottom:10px;width:31px;height:50px;background:conic-gradient(#17b876 0 72%,#e6edf0 72%)}.dashboard-thumb-live{position:relative;min-height:115px;background:#f2f5f7;border-bottom:1px solid #d8e1e6;overflow:hidden}.dashboard-thumb-live>div{position:relative}.thumb-empty{position:absolute;inset:0;display:grid;place-items:center;color:#999;font-size:11px}result-menu div{display:flex;justify-content:space-between;border-bottom:1px solid #eee;padding:10px 4px}.result-menu span{background:#edf1f3;border-radius:10px;padding:2px 7px}.text-button{border:0;background:transparent;color:#008ed2;margin-top:12px}.score{display:flex;align-items:center;justify-content:center;gap:10px;padding:10px}.score b{width:55px;height:55px;border-radius:50%;border:5px solid #32bb81;display:grid;place-items:center;font-size:18px}.score span{color:#16945d}.validation{padding-left:10px;list-style:none;line-height:2;color:#178f5a}.preview-shell{border:1px solid var(--line);background:white;margin:-2px}.preview-toolbar{height:45px;display:flex;align-items:center;justify-content:space-between;padding:0 10px;border-bottom:1px solid var(--line)}.preview-toolbar>div{display:flex;align-items:center;gap:8px}.preview-toolbar button{border:1px solid #d1d5d8;background:white;height:29px;padding:0 10px}.preview-main{display:grid;grid-template-columns:1fr 300px;height:655px}.cube-canvas{position:relative;background:#f2f4f5;border-right:1px solid var(--line);overflow:hidden}.cube-top{position:absolute;left:0;right:0;top:0;height:32px;background:white;border-top:3px solid var(--blue2);border-bottom:1px solid #cfd4d8;display:flex;align-items:center;padding:0 10px;gap:15px}.cube-top b{color:#087fc0}.cube-top i{margin-left:auto;font-style:normal;color:#777}.cube-side{position:absolute;left:0;top:32px;bottom:0;width:130px;background:#edf0f1;display:flex;flex-direction:column}.cube-side strong{padding:15px 10px}.cube-side button{height:37px;border:0;background:transparent;text-align:left;padding-left:12px}.cube-side button.active{background:#d6effb;color:#0088c9}.cube-content{position:absolute;left:130px;top:32px;right:0;bottom:0;padding:12px}.cube-content>header{display:flex;justify-content:space-between;align-items:center}.cube-content>header h2{font-size:16px;margin:5px 0 8px}.cube-content>header span,.cube-content>header small{font-size:9px;color:#888}.cube-kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:7px}.cube-kpis>div{height:78px;background:white;border:1px solid #d6dade;padding:9px;position:relative;display:flex;flex-direction:column}.cube-kpis span{font-size:9px;color:#008dcd}.cube-kpis b{font-size:20px;margin:5px 0}.cube-kpis small{font-size:9px;color:#888}.spark{position:absolute;right:9px;bottom:14px;width:42px;height:18px;border-top:3px solid #13a7e5;transform:skewY(-12deg);border-radius:50%}.spark.s1{transform:skewY(8deg)}.spark.s2{border-color:#f0a000}.cube-grid{display:grid;grid-template-columns:1.7fr .8fr;gap:7px;margin-top:7px}.cube-grid>section{background:white;border:1px solid #d6dade;padding:10px}.cube-grid h3{font-size:11px;margin:0 0 8px}.cube-grid h3 span,.cube-grid h3 button{float:right;border:0;background:transparent;color:#777;font-size:9px}.line-chart{height:175px;position:relative}.line-chart>div{position:absolute;inset:0 0 20px;background:repeating-linear-gradient(to bottom,#fff 0,#fff 29px,#e8ecee 30px)}.line-chart svg{position:absolute;left:0;right:0;top:0;height:145px;width:100%}.line-chart footer{position:absolute;left:0;right:0;bottom:0;display:flex;justify-content:space-between;font-size:8px;color:#999}.donut{width:115px;height:115px;border-radius:50%;background:conic-gradient(#1abc78 0 80%,#f2aa24 80% 92%,#aeb7bd 92%);margin:13px auto;display:grid;place-items:center}.donut:before{content:"";position:absolute;width:72px;height:72px;background:white;border-radius:50%}.donut b{z-index:1;font-size:21px;display:flex;flex-direction:column;align-items:center}.donut small{font-size:8px;color:#888}.donut-panel footer{display:flex;justify-content:space-around;font-size:8px}.donut-panel footer i{display:inline-block;width:7px;height:7px;border-radius:50%;margin-right:3px}.green{background:#1abc78}.orange{background:#f2aa24}.gray{background:#aeb7bd}.alarm-panel{grid-column:1/-1}.alarm-panel table{border-collapse:collapse;width:100%;font-size:9px}.alarm-panel td{border-top:1px solid #e5e8ea;padding:8px}.inspector{background:white;overflow:auto}.inspector>nav{height:42px;display:flex;border-bottom:1px solid var(--line)}.inspector>nav button{flex:1;border:0;border-bottom:2px solid transparent;background:white;font-size:11px}.inspector>nav button.active{color:#008ed2;border-bottom-color:#008ed2}.inspector-body{padding:14px}.inspector-body h3{font-size:15px}.inspector-body p{font-size:11px;color:#777;line-height:1.7}.inspector-body dl{display:grid;grid-template-columns:85px 1fr;border-top:1px solid #eee;margin:15px 0}.inspector-body dt,.inspector-body dd{margin:0;padding:9px 0;border-bottom:1px solid #eee;font-size:10px}.inspector-body dt{color:#888}.component{padding:8px;border:1px solid #e2e5e7;margin-top:6px}.component span{float:right}.code-body header{text-align:right}.code-body header button{border:0;background:transparent;color:#008ed2}.code-body pre{font-size:10px;line-height:1.55;background:#202a33;color:#cce9f7;padding:12px;min-height:410px;overflow:auto}.inspector-body textarea{width:100%;height:120px;border:1px solid #ccd2d6;padding:9px;resize:none}.suggestion{border:1px solid #cfd5d9;background:#f8fafb;border-radius:12px;padding:5px 9px;margin:0 5px 7px 0;font-size:10px}.inspector-body>.button{width:100%;margin:12px 0}.sticky-actions{position:fixed;z-index:20;left:232px;right:0;bottom:0;height:54px;background:white;border-top:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;padding:0 20px;box-shadow:0 -2px 6px #0000000c}.toast{position:fixed;z-index:100;right:24px;top:56px;background:#26323a;color:white;padding:12px 18px;box-shadow:0 5px 18px #0003}.toast b{color:#46d195;margin-right:8px}.modal-backdrop{position:fixed;z-index:90;inset:0;background:#0007;display:grid;place-items:center}.modal{width:610px;max-height:78vh;background:white;box-shadow:0 10px 30px #0004;display:flex;flex-direction:column}.modal>header{height:47px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;padding:0 15px}.modal h2{font-size:15px}.modal>header button{border:0;background:transparent;font-size:22px;color:#888}.modal-body{padding:16px;overflow:auto}.modal>footer{border-top:1px solid var(--line);padding:10px 15px;text-align:right}.modal-note{background:#edf8fd;border-left:3px solid var(--blue);padding:12px;margin-bottom:14px;color:#26799f}.impact-title{font-weight:bold;margin-bottom:8px}.impact-list{list-style:none;padding:0;border:1px solid var(--line)}.impact-list li{display:flex;justify-content:space-between;padding:10px;border-bottom:1px solid #eee}.impact-list span{color:#008ed2}.download-list{display:flex;flex-direction:column}.download-list label{padding:11px;border-bottom:1px solid #eee;display:flex;gap:8px}.download-list>div{margin-top:15px;background:#f4f6f7;padding:12px;display:flex;justify-content:space-between}.history-list>div{display:flex;align-items:center;gap:10px;border-bottom:1px solid #eee;padding:11px}.history-list i{font-style:normal;width:34px;height:34px;border-radius:50%;background:#e4f4fb;color:#008ed2;display:grid;place-items:center}.history-list span{display:flex;flex:1;flex-direction:column;gap:4px}.history-list small{color:#888}.history-list button{border:0;background:transparent;color:#008ed2}.login-page{min-height:100vh;background:linear-gradient(145deg,#eef5f8,#fff 55%,#e5f4fb);border-top:4px solid var(--blue2);display:flex;align-items:center;justify-content:center}.login-top{position:absolute;left:18px;top:14px;display:flex;align-items:center;gap:10px}.login-top b{font-size:18px}.login-top span{border-left:1px solid #ccc;padding-left:10px;color:#777}.login-card{width:420px;background:white;border:1px solid #d6dce0;box-shadow:0 12px 35px #1e526618;padding:42px;text-align:center}.login-logo{font-size:48px}.login-card h1{font-size:23px}.login-card p{color:#777;line-height:1.6}.sso-button{width:100%;height:48px;margin:22px 0 12px;border:1px solid #ccd3d7;background:#fff;display:flex;align-items:center;gap:12px;padding:0 13px}.sso-button b{flex:1;text-align:left}.sso-button i{font-style:normal;font-size:18px;color:#008ed2}.login-card>small{color:#999}.login-page>footer{position:absolute;bottom:18px;color:#999;font-size:10px}
-.avatar-button{border:0;padding:0}.avatar-button:hover{outline:1px solid var(--blue)}
+.avatar-button{border:0;padding:0}
+.design-link{margin-left:auto;color:var(--blue);font-size:12px;text-decoration:none;padding:0 10px;border-right:1px solid #d4d7da}
+.design-link:hover{text-decoration:underline}.avatar-button:hover{outline:1px solid var(--blue)}
 .steps{grid-template-columns:repeat(5,1fr)}
 .module-switch{padding:10px 9px 6px;border-bottom:1px solid var(--line);display:grid;gap:6px}.module-switch>button{height:47px;border:1px solid #d8dde1;background:#fff;display:flex;align-items:center;text-align:left;padding:0 9px;gap:9px}.module-switch>button>b{width:23px;height:23px;display:grid;place-items:center;background:#eff3f5;color:#6f7d85}.module-switch>button>span{display:flex;flex-direction:column;font-weight:bold;gap:2px}.module-switch>button small{font-weight:normal;font-size:9px;color:#8d959a}.module-switch>button.active{border-color:#8ccce9;background:#e9f6fc;color:#007fbd}.module-switch>button.active>b{background:#009fe3;color:white}.side-nav>.side-title{display:none}
 .domain-hero{min-height:160px;background:linear-gradient(120deg,#075d8b,#009bdc 58%,#55c8df);color:white;margin-bottom:14px;padding:26px 30px;display:flex;align-items:center;justify-content:space-between;position:relative;overflow:hidden}.domain-hero:after{content:"◆";position:absolute;right:34%;top:-55px;font-size:230px;color:#ffffff0b;transform:rotate(16deg)}.domain-hero>div:first-child{max-width:640px;z-index:1}.domain-hero .eyebrow{font-size:9px;letter-spacing:2px;opacity:.8}.domain-hero h2{font-size:24px;margin:9px 0}.domain-hero p{margin:0;line-height:1.7;opacity:.86}.domain-hero-stats{display:grid;grid-template-columns:1fr 1fr;gap:1px;background:#ffffff38;z-index:1}.domain-hero-stats div{width:105px;height:58px;background:#ffffff14;display:flex;flex-direction:column;align-items:center;justify-content:center}.domain-hero-stats b{font-size:20px}.domain-hero-stats span{font-size:9px;opacity:.8}.domain-filter label{width:300px}.domain-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;padding:14px 0 4px}.domain-grid article{border:1px solid var(--line);border-top:3px solid #83949c;padding:15px;min-height:230px;background:white}.domain-grid article.water{border-top-color:#009fe3}.domain-grid article.gas{border-top-color:#e58c35}.domain-grid article.energy{border-top-color:#e7b61d}.domain-grid article.dc{border-top-color:#6b70bf}.domain-grid article>header{display:flex;justify-content:space-between;align-items:center}.domain-grid article>header>span{font-size:28px;color:#168ebf}.domain-grid article>header>div{display:flex;align-items:center;gap:7px}.domain-grid h3{font-size:16px;margin:13px 0 6px}.domain-grid p{color:#70787d;font-size:10px;line-height:1.65;min-height:34px}.domain-metrics{display:grid;grid-template-columns:1fr 1fr;border-top:1px solid #edf0f2;border-bottom:1px solid #edf0f2;padding:8px 0;gap:7px}.domain-metrics span{font-size:9px;color:#888}.domain-metrics b{font-size:13px;color:#333;margin-right:3px}.domain-grid footer{display:flex;justify-content:space-between;margin-top:12px}.domain-grid footer button{border:0;background:transparent;color:#008ed2;font-size:10px}.domain-grid footer button:last-child{border:1px solid #a9d8ed;padding:5px 9px}.solution-parts{display:grid;grid-template-columns:repeat(5,1fr);gap:10px}.solution-parts>div{border:1px solid #dfe3e6;background:#fafbfb;padding:12px;display:flex;gap:9px;min-height:80px}.solution-parts i{font-style:normal;font-size:20px;color:#008ed2}.solution-parts span{display:flex;flex-direction:column;gap:6px}.solution-parts small{font-size:9px;line-height:1.5;color:#777}
index 2f838060c0a67e3b44bfcef4e10175bbca050e59..41562ff39e2055124c04e8c6a900c05dcf29e940 100644 (file)
@@ -370,7 +370,7 @@ export default function Home(){
   if(page==="login")return <Login onLogin={()=>go("projects")}/>;
 
   return <div className="app-shell">
-    <header className="topbar"><button className="hamburger">☰</button><div className="brand-mark">⬡</div><strong>iStation</strong><span className="divider"/><span>iStation Team</span><span className="divider"/><span>StationBuilder⌄</span><div className="top-actions"><button title="查看内部登录页" className="avatar avatar-button" onClick={()=>go("login")}>W</button><small>wang.yu@advantech.com<br/>iStation Team</small></div></header>
+    <header className="topbar"><button className="hamburger">☰</button><div className="brand-mark">⬡</div><strong>iStation</strong><span className="divider"/><span>iStation Team</span><span className="divider"/><span>StationBuilder⌄</span><div className="top-actions"><a className="design-link" href="/design-doc.html" target="_blank" rel="noopener">设计方案</a><button title="查看内部登录页" className="avatar avatar-button" onClick={()=>go("login")}>W</button><small>wang.yu@advantech.com<br/>iStation Team</small></div></header>
     <aside className="side-nav"><div className="module-switch"><button className={(["projects","basic","assets","functions","generate","result","preview"] as PageKey[]).includes(page)?"active":""} onClick={()=>go("projects")}><b>▦</b><span>StationBuilder<small>站点生成工具</small></span></button><button className={(["domains","domainDetail"] as PageKey[]).includes(page)?"active":""} onClick={()=>go("domains")}><b>◆</b><span>领域方案库<small>行业标准方案</small></span></button><button className={page==="styleLibrary"?"active":""} onClick={()=>go("styleLibrary")}><b>❖</b><span>页面样式库<small>页面视觉样式</small></span></button></div>{page==="styleLibrary"?<><small>样式库导航</small><button className={page==="styleLibrary"?"active":""} onClick={()=>go("styleLibrary")}><b>❖</b>全部页面样式</button></>:(["domains","domainDetail"] as PageKey[]).includes(page)?<><small>方案库导航</small><button className={page==="domains"?"active":""} onClick={()=>go("domains")}><b>▦</b>全部领域方案</button><button className={page==="domainDetail"?"active":""} onClick={()=>go("domainDetail")}><b>◇</b>方案详情</button><small>领域分类</small>{["燃气","水务 · 自来水厂","新能源","数据中心"].map((x,i)=><button key={x} className={i===1&&page==="domainDetail"?"active":""} onClick={()=>i===1?go("domainDetail"):notify(x+"方案预览已切换")}><b>{["♨","≋","☀","▥"][i]}</b>{x}</button>)}</>:<><small>站点列表</small><button className={page==="projects"?"active":""} onClick={()=>go("projects")}><b>▦</b>站点项目</button><small>站点详情</small><select className="site-switcher" value={siteId ?? ""} onChange={e=>{const v=e.target.value; if(v){ const t=(page==="projects"||["domains","domainDetail","styleLibrary"].includes(page))?"basic":page; go(t, Number(v)); }}}><option value="">选择站点…</option>{sites.map(s=><option key={s.id} value={s.id}>{s.name||"未命名站点"}</option>)}</select><small>生成向导</small>{(["basic","assets","functions","generate"] as PageKey[]).map((item,index)=><button key={item} className={page===item?"active":""} onClick={()=>go(item)}><em>{index+1}</em>{meta[item].label}</button>)}<small>方案交付</small><button className={page==="result"?"active":""} onClick={()=>go("result")}><b>◫</b>站点结果</button><button className={page==="preview"?"active":""} onClick={()=>go("preview")}><b>▣</b>页面预览</button></>}<div className="side-help">
     <b>内部工具 · v0.3</b>
     <span className="model-trigger" onClick={() => setModelOpen(o => !o)}>
diff --git a/public/design-doc.html b/public/design-doc.html
new file mode 100644 (file)
index 0000000..7da01e0
--- /dev/null
@@ -0,0 +1,358 @@
+<!doctype html>
+<!--
+  维护说明:
+  - 本文档是 StationBuilder 的产品/交互设计文档,单一 HTML 源,位于 public/design-doc.html,
+    运行时通过顶栏「设计方案」链接(/design-doc.html)打开。
+  - 每次功能改动后,请同步更新对应章节;新增/遗留的未实现能力用 <span class="todo">尚未实现</span> 标注。
+  - 顶部三个按钮可分别导出 PDF(浏览器打印)、Word(.doc)、Markdown(由本页 DOM 实时转换)。
+-->
+<html lang="zh-CN">
+<head>
+<meta charset="utf-8" />
+<meta name="viewport" content="width=device-width,initial-scale=1" />
+<title>StationBuilder 设计方案</title>
+<style>
+  :root{ --blue:#009fe3; --line:#e3e6e8; --text:#333; --muted:#7a8086; --todo:#c0392b; }
+  *{ box-sizing:border-box; }
+  html,body{ margin:0; background:#fff; color:var(--text);
+    font-family:Arial,"Microsoft YaHei",-apple-system,"Segoe UI",sans-serif; font-size:14px; line-height:1.75; }
+  .doc-toolbar{ position:sticky; top:0; z-index:20; background:#fff; border-bottom:1px solid var(--line);
+    display:flex; align-items:center; justify-content:space-between; padding:8px 16px; }
+  .doc-toolbar b{ font-size:15px; }
+  .doc-toolbar .tools button{ font:inherit; margin-left:8px; padding:5px 12px; border:1px solid var(--blue);
+    background:var(--blue); color:#fff; border-radius:3px; cursor:pointer; }
+  .doc-toolbar .tools button.secondary{ background:#fff; color:var(--blue); }
+  main#doc{ max-width:900px; margin:0 auto; padding:24px 20px 80px; }
+  h1{ font-size:24px; margin:8px 0 4px; }
+  h2{ font-size:19px; margin:34px 0 10px; padding-bottom:6px; border-bottom:2px solid var(--line); }
+  h3{ font-size:16px; margin:24px 0 8px; }
+  h4{ font-size:14px; margin:16px 0 6px; color:#222; }
+  p{ margin:8px 0; }
+  a{ color:var(--blue); text-decoration:none; }
+  a:hover{ text-decoration:underline; }
+  code,pre{ font-family:"SFMono-Regular",Consolas,Menlo,monospace; font-size:12.5px; }
+  code{ background:#f4f5f6; padding:1px 5px; border-radius:3px; }
+  pre{ background:#f7f8f9; border:1px solid var(--line); border-radius:4px; padding:12px; overflow:auto; white-space:pre-wrap; }
+  ul,ol{ margin:8px 0; padding-left:22px; }
+  li{ margin:4px 0; }
+  table{ border-collapse:collapse; width:100%; margin:10px 0; font-size:13px; }
+  th,td{ border:1px solid var(--line); padding:7px 9px; text-align:left; vertical-align:top; }
+  th{ background:#f5f7f8; font-weight:bold; }
+  .muted{ color:var(--muted); }
+  .tag{ display:inline-block; font-size:11px; padding:1px 7px; border-radius:10px; background:#eef7fb; color:#007fbd; border:1px solid #cfeaf6; margin-left:6px; }
+  .todo{ display:inline-block; font-size:11px; padding:1px 7px; border-radius:10px; color:var(--todo); border:1px solid #f0c9c2; background:#fdf0ee; margin-left:6px; }
+  .note{ background:#f7f9fa; border-left:3px solid var(--blue); padding:8px 12px; margin:10px 0; color:#555; }
+  .flow{ background:#fafbfc; border:1px solid var(--line); border-radius:4px; padding:12px 14px; }
+  .flow ol{ counter-reset:step; }
+  .kbd{ border:1px solid #ccc; border-bottom-width:2px; border-radius:3px; padding:0 5px; font-size:12px; background:#fafafa; }
+  @media print{ .doc-toolbar{ display:none; } main#doc{ max-width:none; padding:0; } }
+</style>
+</head>
+<body>
+<div class="doc-toolbar">
+  <b>StationBuilder · 设计方案</b>
+  <div class="tools">
+    <button onclick="downloadPDF()">下载 PDF</button>
+    <button class="secondary" onclick="downloadWord()">下载 Word</button>
+    <button class="secondary" onclick="downloadMD()">下载 Markdown</button>
+  </div>
+</div>
+
+<main id="doc">
+  <p class="muted">文档版本:v0.3 最后更新:2026-08-17 状态:开发中(演示数据已标注)</p>
+  <h1>StationBuilder 产品与交互设计方案</h1>
+
+  <h2>1. 文档说明</h2>
+  <p>本文档说明 StationBuilder(站点 / 领域方案 AI 生成工具)的整体结构、所有页面与菜单、每个交互元素的功能 / 数据来源 / 交互效果,以及端到端的生成流程。面向研发、设计与评审人员。</p>
+  <ul>
+    <li><b>约定</b>:标注 <span class="todo">尚未实现</span> 的项表示已在界面或方案中设计、但当前版本尚未打通能力(多为演示 / 占位)。</li>
+    <li><b>维护</b>:本文件为单一 HTML 源(<code>public/design-doc.html</code>),后续功能改动需同步更新对应章节,未实现项持续标注,直到能力落地后移除标记。</li>
+    <li><b>导出</b>:顶部按钮分别导出 PDF(调用浏览器打印)、Word(<code>.doc</code> 兼容格式)、Markdown(由本页内容实时转换)。</li>
+  </ul>
+
+  <h2>2. 产品概述</h2>
+  <p>StationBuilder 是面向 iStation 团队的内部工具,用于通过 AI(Wordbuddy Hy3)与 <b>dashboard-json-generator Skill</b> 快速生成站点的大屏看板(Cube 仪表盘 JSON)、资产配置、功能菜单与领域方案,并可预览、反馈微调与打包下载。核心用户路径为:<b>站点列表 → 站点详情(基本属性 / 资产 / 功能与菜单 / 生成确认)→ 站点结果 / 页面预览</b>。</p>
+
+  <h2>3. 技术架构与运行环境</h2>
+  <table>
+    <tr><th>层</th><th>技术</th><th>说明</th></tr>
+    <tr><td>前端</td><td>Next.js 16 / React 19 / Vite 8 / Cloudflare Worker(Vinext)</td><td>同源运行;Worker 路由代理 <code>/api/db/*</code> 与 <code>/api/chat</code></td></tr>
+    <tr><td>数据</td><td>SQLite + DB 旁路服务(db-sidecar,:37823)</td><td>站点、Cube、资产、菜单、页面样式、文件等持久化</td></tr>
+    <tr><td>AI</td><td>codebuddy CLI(ai-sidecar :37822 代理 /api/chat;db-sidecar 直接调用)</td><td>以 <code>--output-format stream-json</code> 调用,模型固定 Hy3</td></tr>
+    <tr><td>生成</td><td>dashboard-json-generator Skill</td><td>其 Cube 仪表盘结构规则注入到提示词中,由模型据此生成 JSON <span class="tag">提示词驱动</span></td></tr>
+  </table>
+  <div class="flow">
+    <p><b>数据流</b>:浏览器 → Worker(<code>/api/db</code>) → db-sidecar(SQLite) ;生成时 db-sidecar 直接 <code>spawn</code> codebuddy CLI → 模型+Skill → 解析 JSON 入库 → 以 NDJSON 流式回传进度/日志 → 前端「AI 生成工作流」终端实时显示。</p>
+  </div>
+
+  <h2>4. 整体导航与菜单</h2>
+
+  <h3>4.1 顶栏(Topbar)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>汉堡按钮 ☰</td><td>布局占位(移动端菜单预留)</td><td>—</td><td>当前无行为 <span class="todo">尚未实现</span></td></tr>
+    <tr><td>品牌区 iStation / iStation Team / StationBuilder⌄</td><td>标识当前产品与模块</td><td>静态</td><td>纯展示</td></tr>
+    <tr><td><b>设计方案</b> 链接</td><td>打开本设计文档</td><td>静态链接 <code>/design-doc.html</code></td><td>新标签页打开;位于登录者名称左侧</td></tr>
+    <tr><td>模型选择(Wordbuddy AI · 模型名⌄)</td><td>切换 AI 问答模型</td><td>前端 <code>MODELS</code> 常量</td><td>点击弹出模型列表,仅「免费」模型可选(其余置灰);<b>仅影响 AI 问答,不影响生成(生成固定 Hy3)</b></td></tr>
+    <tr><td>登录者头像 W / 邮箱</td><td>当前登录身份展示;返回登录页</td><td>静态演示</td><td>点击头像回到内部登录页 <span class="todo">登录态尚未实现</span></td></tr>
+  </table>
+
+  <h3>4.2 左侧模块切换(module-switch)</h3>
+  <p>三个互斥模块,同一时刻仅一个高亮:</p>
+  <table>
+    <tr><th>模块</th><th>功能</th><th>点击效果</th></tr>
+    <tr><td>StationBuilder</td><td>站点生成工具主模块</td><td>进入「站点项目(站点列表)」</td></tr>
+    <tr><td>领域方案库</td><td>行业领域标准方案沉淀</td><td>进入「领域方案库」页</td></tr>
+    <tr><td>页面样式库</td><td>可复用的 Cube 视觉样式</td><td>进入「页面样式库」页</td></tr>
+  </table>
+
+  <h3>4.3 站点列表 / 站点详情 分组与站点切换器</h3>
+  <p>StationBuilder 模块下分两类:</p>
+  <ul>
+    <li><b>站点列表</b>:站点项目页,负责站点的查看、搜索、编辑与删除(含批量删除)。</li>
+    <li><b>站点详情</b>:含一个「站点切换器」下拉框(列出全部站点),选择即进入该站点详情;其下为生成向导(基本属性 / 资产 / 功能与菜单 / 生成确认)与方案交付(站点结果 / 页面预览)。</li>
+  </ul>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>站点切换器 &lt;select&gt;</td><td>在已建站点间快速切换</td><td>GET /api/db/sites</td><td>选择后加载该站点并跳转到对应详情页(列表页选择则进入基本属性)</td></tr>
+    <tr><td>步骤条 Steps(基本/资产/功能/生成)</td><td>向导进度指示</td><td>当前页 state</td><td>已完成步骤可点击回跳;当前步高亮、未达步骤置灰</td></tr>
+  </table>
+
+  <h2>5. 页面详细说明</h2>
+
+  <h3>5.1 内部登录(login)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>「使用公司账号登录」按钮</td><td>模拟登录进入主界面</td><td>静态</td><td>点击进入站点项目页 <span class="todo">真实账号体系尚未实现</span></td></tr>
+  </table>
+
+  <h3>5.2 站点项目 / 站点列表(projects)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>统计条(全部/草稿/生成中/生成完成/部分失败)</td><td>站点总量概览</td><td>GET /api/db/sites 聚合</td><td>纯展示</td></tr>
+    <tr><td>搜索框、领域筛选、状态筛选</td><td>按名称 / 领域 / 状态过滤</td><td>前端过滤(sites 列表)</td><td>输入/选择即前端过滤 <span class="todo">服务端查询尚未实现</span></td></tr>
+    <tr><td>视图切换(表格 ☷ / 卡片 ▦)</td><td>列表或卡片两种展示</td><td>本地 state</td><td>切换展示形态</td></tr>
+    <tr><td>批量删除按钮</td><td>删除已勾选站点</td><td>DELETE /api/db/sites</td><td>勾选后出现;二次确认后删除并刷新</td></tr>
+    <tr><td>表头全选框 / 行勾选框</td><td>多选站点</td><td>本地 state</td><td>切换选中集合</td></tr>
+    <tr><td>站点名称链接</td><td>进入站点详情</td><td>—</td><td>点击按状态跳转:已生成→站点结果,否则→基本属性(带 siteId)</td></tr>
+    <tr><td>行「删除」</td><td>删除单个站点</td><td>DELETE /api/db/sites</td><td>二次确认后删除</td></tr>
+    <tr><td>「+ 新建站点」</td><td>创建空白站点</td><td>POST /api/db/sites</td><td>创建后进入基本属性页</td></tr>
+    <tr><td>↻ 刷新</td><td>刷新列表</td><td>—</td><td>提示「列表已刷新」(实际未重新拉取) <span class="todo">尚未实现</span></td></tr>
+  </table>
+
+  <h3>5.3 站点基本属性(basic)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>站点名称(最多 20 字)</td><td>站点命名</td><td>PUT /api/db/sites</td><td>输入即保存(自动);超限标红</td></tr>
+    <tr><td>所属领域(输入 + datalist + 新增领域)</td><td>选择或新建领域</td><td>GET/POST /api/db/domains</td><td>输入匹配历史领域;「+ 新增领域」展开输入(最多 6 字)后保存</td></tr>
+    <tr><td>站点基本情况描述 / 补充要求</td><td>生成上下文</td><td>PUT /api/db/sites</td><td>输入即保存;作为生成提示词的关键上下文</td></tr>
+    <tr><td>参考资料上传</td><td>上传需求文档 / 清单 / 图片</td><td>POST /api/db/sites/:id/files</td><td>选择文件后上传,列出可下载文件</td></tr>
+    <tr><td>填写建议面板</td><td>引导填写</td><td>静态</td><td>纯展示</td></tr>
+  </table>
+
+  <h3>5.4 Asset 资产设置(assets)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>KPI(设备类型/实例/层级)</td><td>资产概览</td><td>本地计算</td><td>随表格输入实时变化</td></tr>
+    <tr><td>设备资产表格(类型/数量/命名规则/操作)</td><td>配置设备类型与数量</td><td>PUT /api/db/sites</td><td>行内编辑即保存;可增删行</td></tr>
+    <tr><td>资产层级预览</td><td>预览「站点→类型→实例」三级结构</td><td>本地计算</td><td>按命名规则展开实例 <span class="todo">3 层结构为展示占位,资产层级建模尚未实现</span></td></tr>
+  </table>
+
+  <h3>5.5 功能与菜单(functions)</h3>
+  <p>三个子页签:</p>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>MU 标准页面(复选)</td><td>选择标准页面</td><td>GET /api/db/mu-tree</td><td>勾选即保存(PUT)</td></tr>
+    <tr><td>Cube 扩展页面表格(名称/内容/生成文件/操作)</td><td>定义并管理扩展页面</td><td>GET/POST/PUT/DELETE /api/db/sites/:id/cubes</td><td>行内编辑、生成、保存、删除、下载;可添加页面</td></tr>
+    <tr><td>「生成」单页按钮</td><td>单独生成某个 Cube</td><td>POST /api/db/sites/:id/cubes/:cid/generate</td><td>触发生成(该路径为非流式,无实时日志) <span class="tag">与生成确认页不同</span></td></tr>
+    <tr><td>站点页面样式选择器</td><td>为全站 Cube 套用视觉样式</td><td>GET /api/db/page-styles;PUT /api/db/sites</td><td>选择样书后应用到站点(写入 style_id),生成时参考其主题</td></tr>
+    <tr><td>菜单结构编辑器</td><td>配置站点菜单(引用 MU / Cube)</td><td>PUT /api/db/sites/:id/menu</td><td>增删菜单项、选择页面类型与引用;保存菜单</td></tr>
+  </table>
+
+  <h3>5.6 生成确认(generate)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>生成范围确认</td><td>展示本次生成范围(站点/资产/MU/Cube)</td><td>当前 site 计算</td><td>「返回修改」占位 <span class="todo">尚未实现</span></td></tr>
+    <tr><td>页面生成提示词(站点级 / 每页)</td><td>生成前核对组合提示词</td><td>前端本地拼装(与后端 buildCubePrompt 同源逻辑)</td><td>「查看提示词」打开模态框,可复制</td></tr>
+    <tr><td>生成选项(复选)</td><td>选择交付物范围</td><td>静态</td><td>勾选态;仅「生成 Asset Excel」置灰 <span class="todo">Asset Excel 尚未实现</span>,其余默认勾选</td></tr>
+    <tr><td>生成任务面板(进度条 / 任务列表)</td><td>展示生成进度</td><td>POST /api/db/sites/:id/generate-all 流式</td><td>NDJSON 逐事件更新任务状态与百分比</td></tr>
+    <tr><td><b>AI 生成工作流</b> 终端</td><td>只读实时展示 CLI/AI/Skill 过程</td><td>同一流式响应中的 <code>{log}</code> 事件</td><td>模型实时输出与 Skill 调用标记逐行滚动显示;点开始生成时清空</td></tr>
+    <tr><td>「开始生成 / 重新生成」</td><td>发起全站生成</td><td>POST generate-all</td><td>禁用直至完成;完成后出现「下载 ZIP」</td></tr>
+    <tr><td>「下载 ZIP」</td><td>下载全部交付物</td><td>GET /api/db/sites/:id/package</td><td>新标签页下载 station-package.zip</td></tr>
+  </table>
+
+  <h3>5.7 站点结果(result)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>结果头(状态 / 修改配置 / 下载全部 / 版本历史)</td><td>概览与操作入口</td><td>当前 site</td><td>「修改配置」回 assets;「下载全部」取 package;「版本历史」开模态</td></tr>
+    <tr><td>KPI 条</td><td>资产/MU/Cube/交付文件统计</td><td>本地计算</td><td>纯展示</td></tr>
+    <tr><td>页面结果卡片(缩略图 + 状态 + 预览/JSON/提示词/修改意见)</td><td>逐页查看与操作</td><td>GET /api/db/sites/:id/cubes/:cid/file</td><td>缩略图由 dashboard-renderer 渲染生成 JSON;各按钮分别预览/下载/看提示词/提修改意见</td></tr>
+    <tr><td>「✎ 修改意见」</td><td>对单页提反馈并重新生成</td><td>PUT 触发 refine</td><td>打开反馈模态,提交后在该页 JSON 基础上按意见重新生成</td></tr>
+  </table>
+
+  <h3>5.8 页面预览(preview)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>页面选择下拉</td><td>切换预览的 Cube 页面</td><td>当前 site.cubes</td><td>切换展示对应页面</td></tr>
+    <tr><td>「站点监控」演示画布</td><td>展示看板式预览外壳</td><td>内置演示数据</td><td>KPI / 图表为<strong>静态演示</strong> <span class="todo">未接入真实生成数据</span></td></tr>
+    <tr><td>「1440×900 / 浅色 / 刷新数据 / 全屏」</td><td>视图与数据操作的占位</td><td>—</td><td>除提示外无实际行为 <span class="todo">尚未实现</span></td></tr>
+    <tr><td>实际 Cube 渲染(info/json/feedback 标签)</td><td>真实渲染已生成 JSON</td><td>GET .../file + dashboard-renderer</td><td>展示真实生成结果(与 result 缩略图同源)</td></tr>
+  </table>
+
+  <h3>5.9 领域方案库(domains)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>领域卡片(燃气/水务/新能源/数据中心)</td><td>领域方案入口</td><td><b>前端硬编码演示数据</b></td><td>「查看方案」仅水务进详情,其余提示占位 <span class="todo">真实方案库尚未接入</span></td></tr>
+    <tr><td>搜索 / 筛选 / 同步方案库</td><td>方案检索</td><td>—</td><td>提示占位 <span class="todo">尚未实现</span></td></tr>
+    <tr><td>「直接使用」</td><td>基于方案建站</td><td>—</td><td>提示并进入基本属性(不真正套用方案模板) <span class="todo">尚未实现</span></td></tr>
+  </table>
+
+  <h3>5.10 方案详情(domainDetail)</h3>
+  <p>以「水务 · 自来水厂」为例的详情展示:资产模板、领域页面、运维流程、菜单、知识等,均为<strong>演示内容</strong>。</p>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>领域页面列表 / 加入站点</td><td>查看并选用领域页面</td><td>硬编码</td><td>「加入站点」仅提示 <span class="todo">尚未实现</span>;「预览」进 preview 占位</td></tr>
+    <tr><td>「使用此方案创建站点」</td><td>从方案建站</td><td>—</td><td>创建空白站点并进入基本属性</td></tr>
+    <tr><td>巡检模板(规划中)</td><td>路线图占位</td><td>硬编码</td><td>标注「规划中」</td></tr>
+  </table>
+
+  <h3>5.11 页面样式库(styleLibrary)</h3>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>样式卡片网格(缩略图 + 名称 + 编辑/删除)</td><td>浏览已有样式</td><td>GET /api/db/page-styles</td><td>缩略图点开大图预览;编辑/删除生效</td></tr>
+    <tr><td>「+ 新增样式」</td><td>上传样式(JSON + 预览图)</td><td>POST /api/db/page-styles</td><td>打开编辑模态,填写名称/预览图/JSON 后保存</td></tr>
+    <tr><td>样式大图预览(StylePreview)</td><td>查看样式原图</td><td>GET /api/db/page-styles/:id/image</td><td>模态展示</td></tr>
+  </table>
+
+  <h2>6. 生成流程(端到端)</h2>
+  <div class="flow">
+    <ol>
+      <li><b>发起</b>:在「生成确认」点「开始生成」→ 前端 <code>POST /api/db/sites/:id/generate-all</code>(携带各 Cube 的名称与内容)。</li>
+      <li><b>落库与准备</b>:db-sidecar 先持久化前端传入的 Cube,再逐页循环。</li>
+      <li><b>调用模型</b>:每页调用 <code>runCodebuddy(prompt, sessionId)</code> → <code>spawn</code> codebuddy CLI(<code>--model hy3</code>、<code>--output-format stream-json</code>、<code>--tools ""</code>)。提示词内含 dashboard-json-generator Skill 的 Cube 结构规则(DASHBOARD_SPEC)与站点/样式上下文。</li>
+      <li><b>解析与校验</b>:从流式输出抽取 JSON → <code>validateDashboard</code> 校验;不通过最多重试 3 次。</li>
+      <li><b>写回</b>:生成的 JSON 存入 gen_files 并关联 Cube,状态置为 generated,同时保存组合提示词。</li>
+      <li><b>打包</b>:全部页面完成后打包为 station-package.zip。</li>
+      <li><b>实时回传</b>:整个过程以 NDJSON 流回传 <code>{task,status}</code> 进度事件与 <code>{log}</code> 文本事件;前端「AI 生成工作流」终端实时滚动显示,任务列表同步更新。</li>
+    </ol>
+  </div>
+  <div class="note">
+    <p><b>模型说明</b>:生成固定使用 Hy3(后端硬编码),与顶栏「模型选择」无关——模型选择仅作用于右下角 AI 问答。每个 Cube 均新起一个 CLI 进程,存在进程冷启动耗时;实时日志可缓解等待时的“无反馈”感。</p>
+    <p><b>单页生成</b>:在「功能与菜单」页对每个 Cube 点「生成」走非流式接口,<b>不</b>提供实时日志,仅结果刷新。</p>
+  </div>
+
+  <h2>7. AI 问答(悬浮按钮 + 对话框)</h2>
+  <table>
+    <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
+    <tr><td>右下角悬浮按钮(AI 图标)</td><td>打开 / 收起 AI 助手</td><td>—</td><td>点击开对话框;<b>可拖动改变位置,双击复位</b>(光标为 pointer,拖动时 grabbing)</td></tr>
+    <tr><td>对话框(模型 · 消息流 / 输入框)</td><td>站点相关问答</td><td>POST /api/chat → ai-sidecar → codebuddy</td><td>流式显示回答;上下文隔离、禁用工具与写操作(仅问答)</td></tr>
+    <tr><td>模型选择弹层</td><td>切换问答模型</td><td>前端 MODELS</td><td>仅免费模型可选</td></tr>
+  </table>
+
+  <h2>8. 模态框(Modal)</h2>
+  <table>
+    <tr><th>类型</th><th>功能</th><th>交互效果</th></tr>
+    <tr><td>提出修改意见(feedback)</td><td>对单页提反馈并重新生成</td><td>展示影响范围,提交后按意见重新生成该页</td></tr>
+    <tr><td>下载站点交付物(download)</td><td>选择下载项</td><td>列表含 Cube JSON / 菜单配置 / 报告;Asset Excel 占位 <span class="todo">尚未实现</span></td></tr>
+    <tr><td>版本历史(history)</td><td>查看版本</td><td>仅展示 V1 当前版本 <span class="todo">真实版本管理尚未实现</span></td></tr>
+    <tr><td>生成提示词(prompt)</td><td>查看/复制组合提示词</td><td>展示发送给模型的完整提示词,可复制到剪贴板</td></tr>
+  </table>
+
+  <h2>9. 数据与存储</h2>
+  <table>
+    <tr><th>对象</th><th>说明</th><th>接口</th></tr>
+    <tr><td>sites</td><td>站点主记录(名称/领域/描述/状态/style_id)</td><td>/api/db/sites</td></tr>
+    <tr><td>cubes</td><td>扩展页面(名称/内容/状态/生成文件)</td><td>/api/db/sites/:id/cubes</td></tr>
+    <tr><td>assets / menus / files / page_styles</td><td>资产 / 菜单 / 参考文件 / 页面样式</td><td>对应 /api/db 路径</td></tr>
+    <tr><td>SQLite 文件</td><td>持久化库(位于仓库 data/db/StationBuilder.sqlite,纳入 Git)</td><td>db-sidecar 读写</td></tr>
+  </table>
+
+  <h2>10. 尚未实现功能汇总</h2>
+  <ul>
+    <li>真实登录 / 账号体系(当前为演示登录) <span class="todo">尚未实现</span></li>
+    <li>领域方案库真实数据与「直接使用 / 加入站点」落地(当前为硬编码演示) <span class="todo">尚未实现</span></li>
+    <li>Asset Excel 生成与下载 <span class="todo">尚未实现</span></li>
+    <li>资产三级层级建模(当前为展示占位) <span class="todo">尚未实现</span></li>
+    <li>页面预览中「站点监控」真实数据接入(当前为静态演示) <span class="todo">尚未实现</span></li>
+    <li>版本历史真实多版本管理(当前仅 V1 占位) <span class="todo">尚未实现</span></li>
+    <li>列表搜索 / 筛选的服务端查询(当前为前端过滤) <span class="todo">尚未实现</span></li>
+    <li>生成模型可切换(当前后端固定 Hy3) <span class="todo">尚未实现</span></li>
+    <li>单页生成(functions 页)的实时日志 <span class="todo">尚未实现</span></li>
+    <li>顶栏汉堡按钮的移动端菜单 <span class="todo">尚未实现</span></li>
+  </ul>
+
+  <h2>11. 维护与导出说明</h2>
+  <ul>
+    <li>功能变更后请同步更新本文档对应章节,未实现项持续标注 <span class="todo">尚未实现</span>,落地后移除。</li>
+    <li><b>下载 PDF</b>:调用浏览器打印,请在打印对话框选择「另存为 PDF」。</li>
+    <li><b>下载 Word</b>:将本文档内容以 Word 兼容的 <code>.doc</code> 格式导出(Word 可直接打开)。</li>
+    <li><b>下载 Markdown</b>:由本页 DOM 实时转换为 Markdown 文本导出。</li>
+  </ul>
+</main>
+
+<script>
+function saveBlob(blob, filename){
+  const url = URL.createObjectURL(blob);
+  const a = document.createElement('a');
+  a.href = url; a.download = filename;
+  document.body.appendChild(a); a.click(); a.remove();
+  setTimeout(()=>URL.revokeObjectURL(url), 60000);
+}
+function downloadPDF(){ window.print(); }
+function downloadWord(){
+  const html = document.getElementById('doc').innerHTML;
+  const head = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns="http://www.w3.org/TR/REC-html40"><head><meta charset="utf-8"></head><body>';
+  const blob = new Blob([''+head+html+'</body></html>'], {type:'application/msword;charset=utf-8'});
+  saveBlob(blob, 'StationBuilder-设计方案.doc');
+}
+function inlineOf(node){
+  let s='';
+  node.childNodes.forEach(n=>{
+    if(n.nodeType===3){ s+=n.textContent; return; }
+    if(n.nodeType!==1) return;
+    const t=n.tagName.toLowerCase();
+    if(t==='strong'||t==='b') s+='**'+n.textContent+'**';
+    else if(t==='em'||t==='i') s+='*'+n.textContent+'*';
+    else if(t==='code') s+='`'+n.textContent+'`';
+    else if(t==='a') s+='['+n.textContent+']('+(n.getAttribute('href')||'')+')';
+    else if(t==='span'){ const cls=n.className; if(cls==='todo') s+='【尚未实现】'; else if(cls==='tag') s+=''; s+=n.textContent; }
+    else s+=inlineOf(n);
+  });
+  return s.replace(/\n+/g,' ').trim();
+}
+function table2md(table){
+  let md=''; const rows=table.querySelectorAll('tr');
+  rows.forEach((tr,i)=>{
+    const cells=[...tr.querySelectorAll('th,td')].map(c=>inlineOf(c).replace(/\|/g,'\\|'));
+    md+='| '+cells.join(' | ')+' |\n';
+    if(i===0) md+='| '+cells.map(()=>'---').join(' | ')+' |\n';
+  });
+  return md;
+}
+function dom2md(el){
+  let md='';
+  el.childNodes.forEach(node=>{
+    if(node.nodeType!==1) return;
+    const tag=node.tagName.toLowerCase();
+    if(tag==='h1') md+='\n# '+inlineOf(node)+'\n\n';
+    else if(tag==='h2') md+='\n## '+inlineOf(node)+'\n\n';
+    else if(tag==='h3') md+='\n### '+inlineOf(node)+'\n\n';
+    else if(tag==='h4') md+='\n#### '+inlineOf(node)+'\n\n';
+    else if(tag==='p') md+=inlineOf(node)+'\n\n';
+    else if(tag==='ul'){ node.querySelectorAll(':scope > li').forEach(li=>{ md+='- '+inlineOf(li)+'\n'; }); md+='\n'; }
+    else if(tag==='ol'){ let n=1; node.querySelectorAll(':scope > li').forEach(li=>{ md+=n+'. '+inlineOf(li)+'\n'; n++; }); md+='\n'; }
+    else if(tag==='table'){ md+=table2md(node)+'\n'; }
+    else if(tag==='pre'){ md+='```\n'+node.textContent+'\n```\n\n'; }
+    else if(tag==='blockquote'){ md+='> '+inlineOf(node)+'\n\n'; }
+    else md+=dom2md(node);
+  });
+  return md;
+}
+function downloadMD(){
+  const md = dom2md(document.getElementById('doc'));
+  const blob = new Blob([md], {type:'text/markdown;charset=utf-8'});
+  saveBlob(blob, 'StationBuilder-设计方案.md');
+}
+</script>
+</body>
+</html>