? <DomainDetail id={openDomainTemplateId==="create"?null:openDomainTemplateId} go={go} notify={notify} onBack={()=>setOpenDomainTemplateId(null)} onCreated={setOpenDomainTemplateId} viewCompletePrompt={viewDtCompletePrompt} viewCompleteAssetsPrompt={viewDtCompleteAssetsPrompt} viewCompleteCubesPrompt={viewDtCompleteCubesPrompt} pageStyles={pageStyles}/>
: <CompanyDomainLibrary go={go} notify={notify} onOpen={(id:number)=>setOpenDomainTemplateId(id)} domainFilter={domainFilter} domainOptions={domainOptions}/>)}
{page==="styleLibrary"&&<CompanyPageStyleLibrary pageStyles={pageStyles} loadPageStyles={loadPageStyles} notify={notify}/>}
- {page==="deviceTemplates"&&<CompanyDeviceTemplates notify={notify} onViewCpPrompt={viewDeviceCpCompletePrompt}/>}
+ {page==="deviceTemplates"&&<CompanyDeviceTemplates notify={notify} onViewCpPrompt={viewDeviceCpCompletePrompt} go={go} siteId={siteId}/>}
{page==="toolSettings"&&<InternalToolSettings model={model} onModelChange={setModel} notify={notify}/>}
{page==="domainSettings"&&<DomainSettings domains={domainOptions} notify={notify} onDeleted={handleDomainDeleted}/>}
{(page==="basic"||(page==="siteEdit"&&siteEditSection==="basic"))&&<Basic site={site} domains={domains} onChange={saveSiteBasic} onAddDomain={addDomain} onUpload={uploadFile} saving={saving} notify={notify} onViewCompletePrompt={viewCompletePrompt} go={go} siteId={siteId} siteHierarchy={siteHierarchy}/>}
</>;
}
-function CompanyDeviceTemplates({notify,onViewCpPrompt}:{notify:(m:string)=>void;onViewCpPrompt:(id:number)=>void}){
+function CompanyDeviceTemplates({notify,onViewCpPrompt,go,siteId}:{notify:(m:string)=>void;onViewCpPrompt:(id:number)=>void;go:(p:PageKey,sid?:number|"new")=>void;siteId:number}){
const [list,setList]=useState<any[]>([]);
const [loading,setLoading]=useState(true);
const [openId,setOpenId]=useState<number|"create"|null>(null);
const [pointFilter,setPointFilter]=useState<""|"configured"|"empty">("");
const [pageIndex,setPageIndex]=useState(1);
const [pageSize,setPageSize]=useState(10);
+ const [fromSite,setFromSite]=useState(false);
const {confirm,el:confirmEl}=useConfirm();
const load=async()=>{ try{ setList(await fetchDb("/device-templates") as any[]); }catch(e:any){ notify("加载失败:"+e.message); }finally{ setLoading(false); } };
useEffect(()=>{ load(); },[]);
// 支持从外部通过 URL 参数 openTemplate 自动打开指定模板编辑页
- useEffect(()=>{ const p=new URLSearchParams(window.location.search); const tid=p.get("openTemplate"); if(tid){ setOpenId(Number(tid)); window.history.replaceState({}, "", "?page=deviceTemplates"); } },[]);
+ useEffect(()=>{ const p=new URLSearchParams(window.location.search); const tid=p.get("openTemplate"); if(tid){ setFromSite(true); setOpenId(Number(tid)); window.history.replaceState({}, "", "?page=deviceTemplates"); } },[]);
const pointCount=(row:any)=>(row.collection_points||[]).length;
const domainNames=(row:any)=>{ try{ return JSON.parse(row.domain_names||"[]") as string[]; }catch{ return []; } };
const remove=async(row:any)=>{ if(!(await confirm(`确定删除设备模板「${row.name}」?`))) return; try{ await fetchDb(`/device-templates/${row.id}`,{method:"DELETE"}); setPreview(null); await load(); notify("设备模板已删除"); }catch(e:any){ notify("删除失败:"+e.message); } };
const counts=useMemo(()=>({all:list.length,configured:list.filter(x=>pointCount(x)>0).length,empty:list.filter(x=>pointCount(x)===0).length}),[list]);
const filtered=useMemo(()=>list.filter(row=>(!pointFilter||(pointFilter==="configured"?pointCount(row)>0:pointCount(row)===0))&&(!query.trim()||`${row.name} ${row.description||""}`.toLowerCase().includes(query.trim().toLowerCase()))),[list,pointFilter,query]);
useEffect(()=>setPageIndex(1),[pointFilter,query]);
- if(openId==="create") return <><div className="resource-back"><button className="text-button" onClick={()=>setOpenId(null)}>← 返回设备模板</button></div><DeviceTemplateCreate notify={notify} onDone={async(id:number)=>{await load();setOpenId(id);}} onCancel={()=>setOpenId(null)}/></>;
+ if(openId==="create") return <><div className="resource-back"><button className="text-button" onClick={()=>{setOpenId(null);if(fromSite)go("siteEdit",siteId);}}>{fromSite?"← 返回站点编辑":"← 返回设备模板列表"}</button></div><DeviceTemplateCreate notify={notify} onDone={async(id:number)=>{await load();setOpenId(id);}} onCancel={()=>setOpenId(null)}/></>;
if(typeof openId==="number"){
const dev=list.find(d=>d.id===openId);
if(!dev) return <div className="empty-state">加载中…</div>;
- return <><div className="resource-back"><button className="text-button" onClick={()=>setOpenId(null)}>← 返回设备模板</button></div><DeviceTemplateCard dev={dev} notify={notify} onChange={load} onBack={()=>setOpenId(null)} onViewCpPrompt={onViewCpPrompt}/></>;
+ return <><div className="resource-back"><button className="text-button" onClick={()=>{setOpenId(null);if(fromSite)go("siteEdit",siteId);}}>{fromSite?"← 返回站点编辑":"← 返回设备模板列表"}</button></div><DeviceTemplateCard dev={dev} notify={notify} onChange={load} onBack={()=>setOpenId(null)} onViewCpPrompt={onViewCpPrompt}/></>;
}
return <>{confirmEl}
<div className="company-status-tabs" role="tablist" aria-label="设备模板状态筛选">{[