feat: introduce Vue Bootstrap interface and scoped router network status

This commit is contained in:
Codex
2026-10-05 20:23:05 +08:00
parent cf895110ee
commit 04d56d5aa2
48 changed files with 4096 additions and 145 deletions
+3
View File
@@ -13,6 +13,7 @@ function homeMove(id,step,target=''){
}
function homeArrangeActions(id,homes){const index=homes.findIndex(h=>h.id===id);return homeArrange?`<div class="home-order-actions"><span>拖动卡片或移动顺序</span><button class="quiet" data-home-move="${esc(id)}" data-step="-1" aria-label="${esc(homes[index].name)}前移" ${index===0?'disabled':''}>← 前移</button><button class="quiet" data-home-move="${esc(id)}" data-step="1" aria-label="${esc(homes[index].name)}后移" ${index===homes.length-1?'disabled':''}>后移 →</button></div>`:'';}
function renderHouseholds(){
if(typeof window!=='undefined'&&window.ZhaoVue){setOptions($('#household-filter'),(state.households||[]).map(h=>`<option value="${esc(h.id)}">${esc(scopeKind(h)+' · '+h.name)}</option>`).join(''));$('#household-filter').value=state.familyId||'';return;}
const saved=state.householdLayout||{revision:0,columns:2,rows:2,groupCity:false,order:[]};
if(homeLayoutOwner!==currentUser?.id){homeLayoutOwner=currentUser?.id||'';homeLayoutDirty=false;homeLayoutDraft=null;homePage=0;homeArrange=false;}
if(!homeLayoutDraft||!homeLayoutDirty&&!homeLayoutSaving&&homeLayoutDraft.revision!==saved.revision)homeLayoutDraft={...saved,order:[...saved.order]};
@@ -38,6 +39,7 @@ function renderHouseholds(){
if(homePresentation==='topology')$('#home-global-topology').innerHTML=`<div class="platform-topology-root"><h3>${esc(typeof branding==='object'?branding.name:'赵府')} · 数字管家</h3><p>${homes.length} 个已授权家庭/场所 · 点击进入内部拓扑</p></div><p class="description">此图表示平台与场所的管理关系;网络标签来自设备档案,连线不代表物理专线或实时在线状态。</p><div class="platform-place-grid">${homes.map(h=>`<button class="platform-place-node" data-home-network="${esc(h.id)}"><span class="badge">${esc(scopeKind(h))}</span><h3>${esc(h.name)}</h3><p>${esc(ZhaoGeography.describe(h)||'地址待填写')}</p><p>${h.deviceCount||0} 台已授权设备</p><small>${(h.networkAccesses||[]).map(x=>esc(networkLabels[x]||'接入线路待确认')).join(' · ')||'尚未登记网络设备'}</small><span>查看场所网络拓扑 →</span></button>`).join('')}</div>`;
for(const card of $$('#household-list .home-card')){card.draggable=homeArrange&&!homeLayoutSaving;card.ondragstart=e=>{if(e.target.closest('button,a,input,select')){e.preventDefault();return;}homeDragId=card.dataset.homeId;e.dataTransfer.setData('text/plain',homeDragId);};card.ondragover=e=>{if(homeArrange&&homeDragId)e.preventDefault();};card.ondrop=e=>{e.preventDefault();if(homeArrange&&homeDragId)homeMove(homeDragId,0,card.dataset.homeId);homeDragId='';};card.ondragend=()=>{homeDragId='';};}
}
if(typeof window==='undefined'||!window.ZhaoVue){
for(const [id,key] of [['home-columns','columns'],['home-rows','rows'],['home-group-city','groupCity']])$('#'+id).onchange=e=>{homeLayoutDraft[key]=key==='groupCity'?e.target.checked:Number(e.target.value);homePage=0;homeLayoutDirty=true;renderHouseholds();};
$('#home-arrange').onchange=e=>{homeArrange=e.target.checked;renderHouseholds();};
$('#home-prev').onclick=()=>{homePage=Math.max(0,homePage-1);renderHouseholds();};$('#home-next').onclick=()=>{homePage++;renderHouseholds();};
@@ -46,6 +48,7 @@ $('#home-global-topology').addEventListener('click',async e=>{const b=e.target.c
$('#home-layout-reset').onclick=()=>{homeLayoutDirty=false;homeLayoutDraft=null;homePage=0;renderHouseholds();};
$('#household-list').addEventListener('click',e=>{const button=e.target.closest('[data-home-move]');if(button)homeMove(button.dataset.homeMove,Number(button.dataset.step));});
$('#home-layout-save').onclick=async()=>{if(homeLayoutSaving||!homeLayoutDirty)return;const generation=sessionGeneration;homeLayoutSaving=true;renderHouseholds();try{const saved=await api('/api/households/layout',homeLayoutDraft);if(generation!==sessionGeneration)return;state.householdLayout=saved;homeLayoutDraft={...saved,order:[...saved.order]};homeLayoutDirty=false;toast('卡片布局与顺序已保存');}catch(e){if(!e.stale)toast(e.message);}finally{homeLayoutSaving=false;if(generation===sessionGeneration)renderHouseholds();}};
}
async function switchHousehold(id){
if(id===state.familyId)return true;
if(!isPlatformAdmin()||!id)return false;