]> acesimba.cloud Git - StationBuilder.git/commitdiff
feat: 设备模版列表→详情 + 全站界面截图嵌入设计文档
authorwang.yu <wang.yu@advantech.com>
Tue, 18 Aug 2026 23:09:21 +0000 (07:09 +0800)
committerwang.yu <wang.yu@advantech.com>
Tue, 18 Aug 2026 23:09:21 +0000 (07:09 +0800)
- 设备模版改为默认展开列表、点击进入详情,支持按名称/说明搜索(v0.8)
- 新增列表视图 CSS(.device-tpl-list / .tpl-thumb / .list-toolbar 等)
- 使用 Chromium + Playwright 自动抓取 11 张页面截图,嵌入设计文档
  5.13「界面截图(UI 总览)」,并替换 5.9/5.10 占位图、补充 5.11/5.12 实图
- 设计文档升至 v0.9,新增 changelog 条目

14 files changed:
app/globals.css
app/page.tsx
public/design-doc-assets/assets.png [new file with mode: 0644]
public/design-doc-assets/basic.png [new file with mode: 0644]
public/design-doc-assets/device-detail.png [new file with mode: 0644]
public/design-doc-assets/device-templates.png [new file with mode: 0644]
public/design-doc-assets/domain-detail.png [new file with mode: 0644]
public/design-doc-assets/domains.png [new file with mode: 0644]
public/design-doc-assets/functions.png [new file with mode: 0644]
public/design-doc-assets/generate.png [new file with mode: 0644]
public/design-doc-assets/login.png [new file with mode: 0644]
public/design-doc-assets/projects.png [new file with mode: 0644]
public/design-doc-assets/style-library.png [new file with mode: 0644]
public/design-doc.html

index a751758678a5fb5364ad4445f2a9e04c75bc1add..f159897786cca5e77b179cd4814166bd834ed963 100644 (file)
 .cp-editor input,.cp-editor select{width:100%;border:1px solid var(--line);border-radius:3px;height:30px;padding:0 7px;background:#fff;font-size:11px}
 .cp-editor input:focus,.cp-editor select:focus{outline:1px solid var(--blue)}
 .cp-range-custom{margin-top:5px}
+/* ===== 设备模版:列表视图 ===== */
+.list-toolbar{margin-bottom:10px}
+.list-toolbar .search{border:1px solid var(--line);border-radius:3px;height:32px;padding:0 10px;width:320px;background:#fff}
+.list-toolbar .search:focus{outline:1px solid var(--blue)}
+.device-tpl-list tbody tr{cursor:pointer}
+.device-tpl-list tbody tr:hover{background:#f5f9fb}
+.tpl-thumb{width:48px;height:34px;object-fit:cover;border:1px solid var(--line);border-radius:3px;display:block;background:#f2f5f7}
+.tpl-thumb-empty{color:#c2c8cc}
 
index 6d86733e29b4d7128f3f45c194cb4b87e1ccd435..03fabbcdfc8fd23a2341bd3e6ddb7d5570ca5136 100644 (file)
@@ -463,7 +463,7 @@ export default function Home(){
         ? <DomainDetail id={openDomainTemplateId==="create"?null:openDomainTemplateId} go={go} notify={notify} onBack={()=>setOpenDomainTemplateId(null)} onCreated={setOpenDomainTemplateId}/>
         : <DomainLibrary go={go} notify={notify} onOpen={(id:number)=>setOpenDomainTemplateId(id)} domainFilter={domainFilter} domainOptions={domainOptions}/>)}
       {page==="styleLibrary"&&<PageStyleLibrary pageStyles={pageStyles} loadPageStyles={loadPageStyles} notify={notify}/>}
-      {page==="deviceTemplates"&&<DeviceTemplates notify={notify} domainOptions={domainOptions}/>}
+      {page==="deviceTemplates"&&<DeviceTemplates notify={notify}/>}
       {page==="basic"&&<Basic site={site} domains={domains} onChange={saveSiteBasic} onAddDomain={addDomain} onUpload={uploadFile} saving={saving} notify={notify} onViewCompletePrompt={viewCompletePrompt}/>}
       {page==="assets"&&<Assets site={site} onChange={saveAssets} saving={saving} notify={notify} onViewCompletePrompt={viewCompleteAssetsPrompt}/>}
       {page==="functions"&&<Functions site={site} muTree={muTree} onMuChange={saveMu} onSiteChange={setSite} pageStyles={pageStyles} notify={notify} onViewCompletePrompt={viewCompleteCubesPrompt}/>}
@@ -1057,14 +1057,14 @@ function DeviceImageField({imageUrl,onPick}:{imageUrl:string|null;onPick:(f:File
   </div>;
 }
 
-function DeviceTemplateCard({dev,notify,onChange}:{dev:any;notify:(m:string)=>void;onChange:()=>void}){
+function DeviceTemplateCard({dev,notify,onChange,onBack}:{dev:any;notify:(m:string)=>void;onChange:()=>void;onBack:()=>void}){
   const [name,setName]=useState(dev.name); const [description,setDesc]=useState(dev.description||"");
   const [imgPreview,setImgPreview]=useState<string|null>(dev.image_path?`/api/db/device-templates/${dev.id}/image`:null);
   const [imgB64,setImgB64]=useState<{filename:string;mime:string;data:string}|null>(null);
   const [cps,setCps]=useState<any[]>((dev.collection_points||[]).map((c:any)=>({...c,key:c.id,checked:false})));
   const onPick=async(f:File)=>{ setImgPreview(URL.createObjectURL(f)); const b=await fileToBase64(f); setImgB64({filename:b.filename,mime:b.mime,data:b.data}); };
   const save=async()=>{ const body:any={name,description}; if(imgB64) body.image=imgB64; await fetchDb(`/device-templates/${dev.id}`,{method:"PUT",headers:{"Content-Type":"application/json"},body:JSON.stringify(body)}); notify("设备已保存"); onChange(); };
-  const delDev=async()=>{ if(!confirm("删除该设备模板?")) return; await fetchDb(`/device-templates/${dev.id}`,{method:"DELETE"}); onChange(); };
+  const delDev=async()=>{ if(!confirm("删除该设备模板?")) return; await fetchDb(`/device-templates/${dev.id}`,{method:"DELETE"}); onChange(); onBack(); };
   const addCpRow=()=>setCps([...cps,{key:-(Date.now()),name:"",collect_note:"",unit:"",value_range:"",description:"",checked:false}]);
   const updCp=(k:number,field:string,val:any)=>setCps(cps.map(c=>c.key===k?{...c,[field]:val}:c));
   const toggleCp=(k:number)=>setCps(cps.map(c=>c.key===k?{...c,checked:!c.checked}:c));
@@ -1120,21 +1120,43 @@ function DeviceTemplateCreate({notify,onDone,onCancel}:{notify:(m:string)=>void;
   </div></Panel>;
 }
 
-function DeviceTemplates({notify,domainOptions}:{notify:(m:string)=>void;domainOptions:{id:number;name:string}[]}){
+function DeviceTemplates({notify}:{notify:(m:string)=>void}){
   const [list,setList]=useState<any[]>([]);
   const [loading,setLoading]=useState(true);
-  const [creating,setCreating]=useState(false);
+  const [openId,setOpenId]=useState<number|"create"|null>(null);
+  const [q,setQ]=useState("");
   const load=async()=>{ try{ setList(await fetchDb("/device-templates") as any); }catch(e:any){ notify("加载失败:"+e.message); } finally{ setLoading(false); } };
   useEffect(()=>{ load(); },[]);
-  return <>
+  const filtered=list.filter(d=>{ if(!q.trim()) return true; const s=q.toLowerCase(); return (d.name||"").toLowerCase().includes(s) || (d.description||"").toLowerCase().includes(s); });
+  const delDev=async(id:number)=>{ if(!confirm("删除该设备模板?")) return; await fetchDb(`/device-templates/${id}`,{method:"DELETE"}); load(); };
+  if(openId===null) return <>
     <div className="domain-hero"><div><span className="eyebrow">DEVICE TEMPLATE LIBRARY</span><h2>设备模版</h2><p>独立维护设备模版及其采集点,编辑后可在领域方案中按需选用。</p></div></div>
-    <Panel title="设备模版" tools={creating?null:<Button onClick={()=>setCreating(true)}>+ 新增设备模版</Button>}>
-      {creating && <DeviceTemplateCreate notify={notify} onDone={()=>{setCreating(false);load();}} onCancel={()=>setCreating(false)}/>}
+    <Panel title="设备模版" tools={<Button onClick={()=>setOpenId("create")}>+ 新增设备模版</Button>}>
+      <div className="list-toolbar"><input className="search" placeholder="搜索设备名称 / 说明" value={q} onChange={e=>setQ(e.target.value)}/></div>
       {loading? <p>加载中…</p> :
-       list.length===0 && !creating? <p className="muted">暂无设备模版,点击「+ 新增设备模版」创建。</p> :
-       <div className="device-list">{list.map((d:any)=><DeviceTemplateCard key={d.id} dev={d} notify={notify} onChange={load}/>)}</div>}
+       filtered.length===0? <p className="muted">{list.length===0?"暂无设备模版,点击「+ 新增设备模版」创建。":"无匹配结果"}</p> :
+       <table className="data-table device-tpl-list"><thead><tr><th>图片</th><th>名称</th><th>说明</th><th>采集点</th><th>操作</th></tr></thead>
+       <tbody>{filtered.map((d:any)=>(
+         <tr key={d.id} onClick={()=>setOpenId(d.id)}>
+           <td>{d.image_path? <img className="tpl-thumb" src={`/api/db/device-templates/${d.id}/image`} alt=""/> : <span className="tpl-thumb-empty">—</span>}</td>
+           <td>{d.name}</td>
+           <td className="muted">{d.description||""}</td>
+           <td>{(d.collection_points||[]).length}</td>
+           <td onClick={e=>e.stopPropagation()}><button className="text-button" onClick={()=>setOpenId(d.id)}>编辑</button> <button className="text-button" onClick={()=>delDev(d.id)}>删除</button></td>
+         </tr>
+       ))}</tbody></table>}
     </Panel>
   </>;
+  if(openId==="create") return <>
+    <div className="domain-detail-head"><button className="text-button" onClick={()=>setOpenId(null)}>← 返回列表</button></div>
+    <DeviceTemplateCreate notify={notify} onDone={async(id:number)=>{ await load(); setOpenId(id); }} onCancel={()=>setOpenId(null)}/>
+  </>;
+  const dev=list.find(d=>d.id===openId);
+  if(!dev) return <p>加载中…</p>;
+  return <>
+    <div className="domain-detail-head"><button className="text-button" onClick={()=>setOpenId(null)}>← 返回列表</button></div>
+    <DeviceTemplateCard dev={dev} notify={notify} onChange={load} onBack={()=>setOpenId(null)}/>
+  </>;
 }
 
 function DeviceTemplatePicker({tmplId,go,selectedIds,notify,onChange}:{tmplId:number;go:(p:PageKey)=>void;selectedIds:number[];notify:(m:string)=>void;onChange:()=>void}){
diff --git a/public/design-doc-assets/assets.png b/public/design-doc-assets/assets.png
new file mode 100644 (file)
index 0000000..cfb723b
Binary files /dev/null and b/public/design-doc-assets/assets.png differ
diff --git a/public/design-doc-assets/basic.png b/public/design-doc-assets/basic.png
new file mode 100644 (file)
index 0000000..2e66b40
Binary files /dev/null and b/public/design-doc-assets/basic.png differ
diff --git a/public/design-doc-assets/device-detail.png b/public/design-doc-assets/device-detail.png
new file mode 100644 (file)
index 0000000..31b1e51
Binary files /dev/null and b/public/design-doc-assets/device-detail.png differ
diff --git a/public/design-doc-assets/device-templates.png b/public/design-doc-assets/device-templates.png
new file mode 100644 (file)
index 0000000..8d73306
Binary files /dev/null and b/public/design-doc-assets/device-templates.png differ
diff --git a/public/design-doc-assets/domain-detail.png b/public/design-doc-assets/domain-detail.png
new file mode 100644 (file)
index 0000000..1f39247
Binary files /dev/null and b/public/design-doc-assets/domain-detail.png differ
diff --git a/public/design-doc-assets/domains.png b/public/design-doc-assets/domains.png
new file mode 100644 (file)
index 0000000..1b6c830
Binary files /dev/null and b/public/design-doc-assets/domains.png differ
diff --git a/public/design-doc-assets/functions.png b/public/design-doc-assets/functions.png
new file mode 100644 (file)
index 0000000..fa2d9e4
Binary files /dev/null and b/public/design-doc-assets/functions.png differ
diff --git a/public/design-doc-assets/generate.png b/public/design-doc-assets/generate.png
new file mode 100644 (file)
index 0000000..22acee7
Binary files /dev/null and b/public/design-doc-assets/generate.png differ
diff --git a/public/design-doc-assets/login.png b/public/design-doc-assets/login.png
new file mode 100644 (file)
index 0000000..1f8f0ff
Binary files /dev/null and b/public/design-doc-assets/login.png differ
diff --git a/public/design-doc-assets/projects.png b/public/design-doc-assets/projects.png
new file mode 100644 (file)
index 0000000..e58f682
Binary files /dev/null and b/public/design-doc-assets/projects.png differ
diff --git a/public/design-doc-assets/style-library.png b/public/design-doc-assets/style-library.png
new file mode 100644 (file)
index 0000000..4e01e3e
Binary files /dev/null and b/public/design-doc-assets/style-library.png differ
index bebce90d2790325a0d200e0c3730d7be41b61889..aacaae313a855a298ac7c4d916935c5fdc7d8472 100644 (file)
@@ -85,7 +85,7 @@
   <nav class="doc-nav"><div class="doc-nav-inner" id="toc"></div></nav>
 
 <main id="doc">
-  <p class="muted">文档版本:v0.7 最后更新:2026-08-18 修改人:wang.yu 状态:开发中(演示数据已标注)</p>
+  <p class="muted">文档版本:v0.9 最后更新:2026-08-18 修改人:wang.yu 状态:开发中(演示数据已标注)</p>
   <h1>StationBuilder 产品与交互设计方案</h1>
 
   <h2>1. 文档说明</h2>
     <tr><td>1 基本属性 / 2 资产 / 3 功能与菜单 / 4 生成确认</td><td>StationBuilder / 生成向导</td><td>生成向导分步导航</td><td>前端 state</td><td>点击 go 对应页;当前步骤高亮;已完成步骤可回跳</td></tr>
     <tr><td>站点结果 / 页面预览</td><td>StationBuilder / 方案交付</td><td>查看生成结果与预览</td><td>—</td><td>点击 go("result" / "preview");当前页高亮</td></tr>
     <tr><td>领域方案</td><td>领域方案库 / 方案库导航</td><td>方案库列表(列表/卡片切换)与详情</td><td>GET /api/db/domain-templates、GET /api/domains</td><td>点击 go("domains");列表页右上角可在「列表 / 卡片」间切换;点击某方案(列表「编辑」或卡片本身)以同页内嵌编辑器打开详情(<code>openDomainTemplateId</code> state 控制)。「+ 新增领域方案」切到新增表单;「清除领域筛选」返回全部</td></tr>
-    <tr><td>设备模版</td><td>领域方案库 / 方案库导航</td><td>独立页面,编辑设备模版与采集点</td><td>GET /api/db/device-templates</td><td>点击 go("deviceTemplates");列表展示全部设备模版,可新增 / 编辑 / 删除;每个设备模版维护名称 / 说明 / 图片 / 所属领域(多选) / 采集点。设备模版在此独立维护,方案详情中通过「选用设备模版」勾选引用</td></tr>
+    <tr><td>设备模版</td><td>领域方案库 / 方案库导航</td><td>独立页面,编辑设备模版与采集点</td><td>GET /api/db/device-templates</td><td>点击 go("deviceTemplates");默认展开<strong>列表视图</strong>(缩略图 / 名称 / 说明 / 采集点数 / 操作),支持按名称或说明搜索;点击行进入该设备模版的<strong>详情编辑</strong>(名称 / 说明 / 图片 / 采集点),可返回列表。列表上方「+ 新增设备模版」进入新建表单。设备模版在此独立维护,方案详情中通过「选用设备模版」勾选引用</td></tr>
     <tr><td>领域分类(动态)</td><td>领域方案库 / 领域分类</td><td>按领域筛选方案列表(二级菜单)</td><td>GET /api/domains(动态渲染)</td><td>点击某领域 → go("domains") 且 <code>domainFilter</code>=该领域 id,列表自动只显示该领域方案;当前领域高亮;「清除领域筛选」返回全部</td></tr>
     <tr><td>全部页面样式</td><td>页面样式库 / 样式库导航</td><td>样式库列表</td><td>GET /api/db/page-styles</td><td>点击 go("styleLibrary");当前页高亮</td></tr>
   </table>
   <h3>5.9 领域方案库(domains)</h3>
   <p>领域方案库已从硬编码演示改为<strong>真实数据库驱动</strong>。列表与详情编辑器同一页面内通过 <code>openDomainTemplateId</code> state 切换:<code>null</code>=列表,<code>"create"</code>=新增表单,<code>number</code>=该方案详情编辑器。列表支持<strong>列表 / 卡片两种视图</strong>切换(右上角视图切换按钮,<code>view</code> state 控制);点击左侧「领域分类」下的某个领域会按 <code>domainFilter</code> 自动筛选该领域方案。</p>
   <p>下图展示了领域方案库列表与「+ 新增领域方案」入口:</p>
-  <figure class="placeholder"><img src="design-doc-assets/placeholder.svg" alt="领域方案库列表"/><figcaption>领域方案库:方案列表与新增入口(示意图,待替换为实际截图)。</figcaption></figure>
+  <figure><img src="design-doc-assets/domains.png" alt="领域方案库列表"/><figcaption>领域方案库:列表视图,支持列表/卡片切换、按领域分类筛选,点击「编辑」进入方案详情。</figcaption></figure>
   <table>
     <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
     <tr><td>领域方案列表表格</td><td>展示全部领域方案(模板名称 / 所属领域 / 版本 / 状态 / 创建人 / 设备·页面·文档数 / 操作)</td><td>GET /api/db/domain-templates(含 domain_name、device_count、page_count、doc_count)</td><td>每行「编辑」→ 打开该方案详情编辑器;「删除」→ 确认后 DELETE /api/db/domain-templates/:id(级联删除 device_template_domain_templates)并刷新;空列表提示「点击右上角新增」</td></tr>
   <h3>5.10 方案详情(domainDetail / 内嵌编辑器)</h3>
   <p>详情为同一页面内嵌编辑器,所有内容均可编辑,数据持久化到领域方案库相关表。各面板如下:</p>
   <p>下图展示了一个领域方案的详情编辑器(模板信息 + 设备模板 + 领域页面等面板):</p>
-  <figure class="placeholder"><img src="design-doc-assets/placeholder.svg" alt="方案详情编辑器"/><figcaption>领域方案详情编辑器(示意图,待替换为实际截图)。</figcaption></figure>
+  <figure><img src="design-doc-assets/domain-detail.png" alt="方案详情编辑器"/><figcaption>领域方案详情编辑器:模板信息、选用设备模版、领域页面、模板菜单、知识文档、运维流程等面板。</figcaption></figure>
   <table>
     <tr><th>面板 / 元素</th><th>功能</th><th>数据来源 / 接口</th><th>交互效果</th></tr>
     <tr><td>模板信息</td><td>编辑所属领域 / 名称 / 版本 / 状态 / 创建人 / 描述</td><td>GET /api/db/domain-templates/:id 详情;PUT /api/db/domain-templates/:id 保存</td><td>「保存模板信息」写回并重新加载</td></tr>
   </table>
 
   <h3>5.11 页面样式库(styleLibrary)</h3>
+  <p>下图为页面样式库的实际界面,展示缩略图卡片网格与「+ 新增样式」入口:</p>
+  <figure><img src="design-doc-assets/style-library.png" alt="页面样式库"/><figcaption>页面样式库:16:9 缩略图卡片网格,可新增 / 编辑 / 删除样式。</figcaption></figure>
   <table>
     <tr><th>元素</th><th>功能</th><th>数据来源</th><th>交互效果</th></tr>
     <tr><td>样式卡片网格(缩略图 + 名称 + 编辑/删除)</td><td>浏览已有样式</td><td>GET /api/db/page-styles</td><td>3 列布局,缩略图按 16:9 比例自适应高度(object-fit:contain,整页完整可见、不被截断);缩略图点开大图预览;编辑/删除生效</td></tr>
   </table>
 
   <h3>5.12 设备模版(deviceTemplates)</h3>
-  <p>独立的「设备模版」页,维护可复用的设备模版与采集点,供领域方案在详情中「选用设备模版」引用。列表为整宽卡片(<code>.device-list</code> 单列),每个卡片即一个设备模版的编辑区。</p>
+  <p>独立的「设备模版」页,维护可复用的设备模版与采集点,供领域方案在详情中「选用设备模版」引用。设备模版数量可能成百上千,因此采用<strong>默认展开列表、点击进入详情</strong>的方式(而非整页卡片栅格):列表为紧凑表格(<code>.device-tpl-list</code>),含缩略图 / 名称 / 说明 / 采集点数 / 操作,并支持按名称或说明实时搜索;点击任意行进入该设备模版的详情编辑区,详情内「← 返回列表」回到列表。列表上方「+ 新增设备模版」进入新建表单(创建后定位到新建设备的详情)。</p>
+  <figure><img src="design-doc-assets/device-templates.png" alt="设备模版列表"/><figcaption>设备模版列表:默认展开表格视图,支持搜索,点击行进入详情编辑。</figcaption></figure>
+  <figure><img src="design-doc-assets/device-detail.png" alt="设备模版详情"/><figcaption>设备模版详情:名称 / 说明 / 设备图片、长方形预览区、采集点表格编辑(新增 / 批量删除 / 保存 / 数值范围下拉)。</figcaption></figure>
   <table>
     <tr><th>元素</th><th>功能</th><th>数据来源 / 接口</th><th>交互效果</th></tr>
     <tr><td>设备图片</td><td>以长方形预览区展示设备图片,点击预览区即上传 / 更换</td><td>GET /api/db/device-templates/:id/image(服务端按 image_path 落盘 <code>data/db/uploads/devices/</code>);PUT /api/db/device-templates/:id(image base64)</td><td>预览区固定高度、<code>object-fit:cover</code> 不再溢出;上传后即时本地预览,保存时写回;删除图片 <span class="todo">尚未实现</span></td></tr>
     <tr><td>设备名称 / 说明</td><td>编辑设备基本信息</td><td>GET /api/db/device-templates/:id;PUT /api/db/device-templates/:id</td><td>「保存设备」写回名称和说明(不再维护设备侧「所属领域」,改为由领域 / 方案侧选用)</td></tr>
     <tr><td>采集点(表格编辑)</td><td>表格方式维护采集点:名称 / 说明 / 单位 / 数值范围 / 描述;可新增一行、勾选删除一行或多行、保存</td><td>GET /api/db/device-templates/:id 的 collection_points;POST /api/db/device-templates/:id/collection-points(新建,带全字段);PUT / DELETE /api/db/collection-points/:id</td><td>「+ 新增一行」追加空白可编辑行;表头复选框全选,行内复选框多选,「删除选中」批量 DELETE;「保存采集点」对新建行 POST、对已有行 PUT;<strong>数值范围</strong>为下拉选择器(预设 0~100 / 0~1000 / -20~80 / -40~85 / 0~50 / 0~5 / 0~1 / 0~100%,或「自定义…」输入)</td></tr>
-    <tr><td>新增设备模版</td><td>新建设备模版(名称 / 说明 / 图片)</td><td>POST /api/db/device-templates</td><td>列表上方「+ 新增设备模版」展开表单,创建后自动出现在列表中</td></tr>
+    <tr><td>新增设备模版</td><td>新建设备模版(名称 / 说明 / 图片)</td><td>POST /api/db/device-templates</td><td>列表上方「+ 新增设备模版」进入新建表单,创建后自动打开该设备模版的详情编辑区</td></tr>
   </table>
 
+  <h3>5.13 界面截图(UI 总览)</h3>
+  <p>以下截图覆盖了当前 StationBuilder 原型中可访问的主要页面。截图在 1440×900 视口下使用 Playwright + Chromium 自动抓取,图片存放于 <code>public/design-doc-assets/</code>。</p>
+
+  <figure><img src="design-doc-assets/login.png" alt="登录页"/><figcaption>登录页:iStation StationBuilder 统一登录入口。</figcaption></figure>
+  <figure><img src="design-doc-assets/projects.png" alt="站点项目"/><figcaption>站点项目:全部站点列表、统计概览、筛选与新建入口。</figcaption></figure>
+  <figure><img src="design-doc-assets/basic.png" alt="站点基本属性"/><figcaption>站点基本属性:站点名称、域名、业务描述、关键词、文件上传等基本信息维护。</figcaption></figure>
+  <figure><img src="design-doc-assets/assets.png" alt="Asset 资产设置"/><figcaption>Asset 资产设置:设备类型、数量、命名规则与资产层级预览。</figcaption></figure>
+  <figure><img src="design-doc-assets/functions.png" alt="功能与菜单"/><figcaption>功能与菜单:MU 页面选择、Cube 扩展页面编辑、页面样式选择、菜单结构预览。</figcaption></figure>
+  <figure><img src="design-doc-assets/generate.png" alt="生成确认"/><figcaption>生成确认:选择生成范围、执行 AI 生成,并实时查看生成进度。</figcaption></figure>
+  <figure><img src="design-doc-assets/domains.png" alt="领域方案库"/><figcaption>领域方案库:列表/卡片视图切换、按领域分类筛选。</figcaption></figure>
+  <figure><img src="design-doc-assets/domain-detail.png" alt="领域方案详情"/><figcaption>领域方案详情:模板信息、选用设备模版、领域页面、模板菜单、知识文档、运维流程占位。</figcaption></figure>
+  <figure><img src="design-doc-assets/device-templates.png" alt="设备模版列表"/><figcaption>设备模版列表:紧凑表格、搜索、点击进入详情。</figcaption></figure>
+  <figure><img src="design-doc-assets/device-detail.png" alt="设备模版详情"/><figcaption>设备模版详情:设备基本信息、图片、采集点表格编辑。</figcaption></figure>
+  <figure><img src="design-doc-assets/style-library.png" alt="页面样式库"/><figcaption>页面样式库:样式缩略图网格与新增/编辑入口。</figcaption></figure>
+
   <h2>6. 生成流程(端到端)</h2>
   <div class="flow">
     <ol>
   <h3>11.1 版本变更记录(Changelog)</h3>
   <table class="changelog">
     <tr><th>版本</th><th>日期</th><th>修改人</th><th>说明</th></tr>
+    <tr><td>v0.9</td><td>2026-08-18</td><td>wang.yu</td><td>使用 Chromium + Playwright 自动抓取全站可访问页面截图,统一嵌入设计文档:新增 5.13「界面截图(UI 总览)」,覆盖登录页、站点项目、站点基本属性、Asset 资产、功能与菜单、生成确认、领域方案库、领域方案详情、设备模版列表/详情、页面样式库(共 11 张)。替换 5.9 / 5.10 中两张 placeholder.svg 为真实截图;在 5.11 / 5.12 补充真实界面图。截图文件置于 public/design-doc-assets/。</td></tr>
+    <tr><td>v0.8</td><td>2026-08-18</td><td>wang.yu</td><td>设备模版改为「默认展开列表、点击进入详情」:列表为紧凑表格(缩略图 / 名称 / 说明 / 采集点数 / 操作),支持按名称或说明实时搜索;点击行进入该设备模版的详情编辑区(名称 / 说明 / 图片 / 采集点),可「← 返回列表」;「+ 新增设备模版」进入新建表单,创建后定位到新建设备的详情。面向成百上千设备模版的可扩展性。同步更新 4.2 导航表、5.12 章节。</td></tr>
     <tr><td>v0.7</td><td>2026-08-18</td><td>wang.yu</td><td>设备模版页交互优化:设备图片改为长方形预览区(点击上传,不再溢出);移除设备侧「所属领域」多选(改为由领域/方案侧选用设备);采集点改为整宽表格编辑,支持新增一行、勾选批量删除、保存,数值范围改为下拉选择器(预设 + 自定义);修复打开卡片时 collection_points 未加载导致的 runtime error,列表接口补充返回 collection_points。后端新增 GET /api/db/device-templates/:id/image 图片服务。新增 5.12 章节。</td></tr>
     <tr><td>v0.6</td><td>2026-08-18</td><td>wang.yu</td><td>领域方案库重构为多菜单:列表页支持「列表 / 卡片」视图切换与按领域分类筛选;新增独立的「设备模版」页(前端 DeviceTemplates,独立维护设备模版与采集点);方案详情的设备面板改为「选用设备模版」(DeviceTemplatePicker,勾选即重写 device_template_domain_templates 关联,未找到可跳转新增);领域分类改为由 GET /api/domains 动态渲染的二级菜单。后端 domain-templates 更新支持 device_template_ids 重关联。同步更新 4.2 / 5.9 / 5.10 章节。</td></tr>
     <tr><td>v0.5</td><td>2026-08-18</td><td>wang.yu</td><td>设计方案支持页面截图:新增 figure / 图注 样式(自动编号),撰写方式参考 MachineCube 手册(小节标题 + 说明 +「下图」引用 + 配图 + 图注);图片置于 public/design-doc-assets/。新增「版本变更记录」,每次修改升级小版本号并登记修改人 / 日期。</td></tr>