'use strict'; pageInfo.households=['我的家庭与场所','家庭、办公室或工厂,分别组织,各自授权。']; const scopeKind=h=>({home:'家庭',office:'办公场所',factory:'工厂'}[h.kind||'home']||'家庭'); let coverSaving=false; function renderHouseholds(){ const homes=state.households||[]; setOptions($('#household-filter'),homes.map(h=>``).join('')); $('#household-filter').value=state.familyId||''; if(activePage!=='households')return; const cards=h=>`
${h.coverUrl?`${esc(h.name)}封面照片`:''}
${h.id===state.familyId?'当前归属 · ':''}${scopeKind(h)}${isAdmin()?``:''}

${esc(h.name)}

${esc(ZhaoGeography.describe(h)||'地区待填写')}

${esc(h.note||'区域与设备归属独立管理')}

${!h.kind||h.kind==='home'?'一家之主':'主要负责人'}
${esc(h.ownerName||'待指定')}
备用负责人
${esc(h.successorName||'待指定')}
账号 / 空间
${h.members} 个 / ${h.sites} 处
${!h.successorId?'

尚未指定备用负责人,建议提前安排可信人员接管。

':''}
${h.canAssignOwner?``:''}
${h.leadershipHistory?.length?`
交接记录${h.leadershipHistory.slice().reverse().map(r=>`

${esc(new Date(r.at).toLocaleString())} · ${esc(r.actor)}
${esc(r.reason)}

`).join('')}
`:''}
`; const groups=new Map();for(const h of homes){const key=[h.geography?.body&&h.geography.body!=='地球'?h.geography.body:'',h.geography?.country||h.country||'',h.province,h.city||'未填写城市/区域'].filter(Boolean).join(' / ');if(!groups.has(key))groups.set(key,[]);groups.get(key).push(h);} $('#household-list').innerHTML=[...groups].sort(([a],[b])=>a.localeCompare(b,'zh-CN')).map(([city,rows])=>`

${esc(city)} ${rows.length} 个家庭或场所

${rows.map(cards).join('')}
`).join(''); } async function switchHousehold(id){ if(id===state.familyId)return true; if(!isPlatformAdmin()||!id)return false; if(typeof confirmJournalDiscard==='function'&&!confirmJournalDiscard())return false; const user=currentUser; // Invalidate outstanding API responses, video players and dialogs before changing scope. clearPrivateView(); householdScope=id;currentUser=user;loggedIn=true; await auth();return loggedIn; } function openHousehold(id){ if(coverSaving){toast('档案仍在保存,请稍候。');return;} if(!isAdmin())return; const item=(state.households||[]).find(h=>h.id===id); if(!item&&!isPlatformAdmin())return; nextImageJob('cover');$('#household-cover-status').dataset.error='false';fillForm($('#household-form'),{profileRevision:0,...(item||{})});coverDraft=undefined;ZhaoGeography.fill($('#household-form'),item||{});$('#household-cover-preview').hidden=!item?.coverUrl;if(item?.coverUrl)$('#household-cover-preview').src=item.coverUrl;else $('#household-cover-preview').removeAttribute('src');$('#household-cover-status').textContent='封面存入百度网盘,原图可选 30 MB 以内;浏览器会先压缩。仅向获授权账号开放。';$('#household-dialog').showModal(); } async function openLeadership(id){ try{ if(id!==state.familyId&&!await switchHousehold(id))return; const home=(state.households||[]).find(h=>h.id===id); if(!home?.canAssignOwner)return; const result=await api('/api/users'); const candidates=result.users.filter(u=>u.role==='family_admin'&&!u.disabled); const f=$('#leadership-form'),options=candidates.map(u=>``).join(''); f.elements.ownerId.innerHTML=''+options; f.elements.successorId.innerHTML=''+options; fillForm(f,{id:home.id,revision:home.leadershipRevision||0,ownerId:home.ownerId||'',successorId:home.successorId||''}); $('#leadership-dialog').showModal(); }catch(error){if(!error.stale)toast(error.message);} } $('#leadership-form').addEventListener('submit',async e=>{ e.preventDefault();const f=e.currentTarget,b=f.querySelector('[type=submit]');b.disabled=true; try{const d=formValues(f);d.revision=Number(d.revision);await api('/api/households/leadership',d);f.closest('dialog').close();await refresh();showPage('households');toast('负责人已更新,交接记录已保存');} catch(error){if(!error.stale)f.querySelector('.form-error').textContent=error.message;}finally{b.disabled=false;} }); $('#household-filter').onchange=async e=>{const switched=await switchHousehold(e.target.value);if(switched&&loggedIn)showPage('overview');else e.target.value=state.familyId;}; $('#new-household').onclick=()=>openHousehold(); $('#household-form').addEventListener('submit',async e=>{ e.preventDefault();const f=e.currentTarget;if(coverSaving)return; if(imageJobPending('cover')){f.querySelector('.form-error').textContent='照片仍在处理,请完成后保存。';return;} if(imageJobs.get('cover')?.error){f.querySelector('.form-error').textContent='封面未准备成功:请重新选择图片,或点击“取消本次图片选择”。';return;} const payload={...formValues(f),profileRevision:Number(f.elements.profileRevision.value||0),...(coverDraft!==undefined?{coverData:coverDraft}:{})}; const controls=[...f.elements],disabled=controls.map(x=>x.disabled),generation=sessionGeneration; coverSaving=true;controls.forEach(x=>x.disabled=true);f.querySelector('.form-error').textContent=payload.coverData?'正在上传封面到百度网盘并校验,请稍候…':'正在保存档案…'; try{ const saved=await api('/api/households',payload);if(generation!==sessionGeneration)return; if(payload.coverData&&!saved.coverRef?.fsId)throw Error('服务器未确认封面保存,请重试。'); // Adopt the returned revision even if a later state refresh fails. f.elements.id.value=saved.id;f.elements.profileRevision.value=saved.profileRevision; const view={...saved,coverUrl:saved.coverData||saved.coverRef?'/api/households/cover?id='+encodeURIComponent(saved.id)+'&v='+saved.coverRevision:''}; delete view.coverData;delete view.coverRef;const previous=(state.households||[]).find(x=>x.id===saved.id); if(previous)Object.assign(previous,view);else (state.households||=[]).push(view); renderHouseholds();f.closest('dialog').close();toast(payload.coverData?'封面已存百度网盘,归属档案已保存':payload.coverData===''?'归属档案已保存,已使用默认封面':'归属档案已保存'); try{await refresh();}catch(error){if(!error.stale&&generation===sessionGeneration)toast('档案已保存,列表刷新暂未完成,请稍后刷新页面。无需重复保存。');} }catch(error){if(!error.stale&&generation===sessionGeneration)f.querySelector('.form-error').textContent=error.message;} finally{coverSaving=false;controls.forEach((x,i)=>x.disabled=disabled[i]);} }); document.addEventListener('click',async e=>{ const b=e.target.closest('button');if(!b)return; if(b.dataset.editHousehold)openHousehold(b.dataset.editHousehold); if(b.dataset.leadership)await openLeadership(b.dataset.leadership); if(b.dataset.enterHousehold){const switched=await switchHousehold(b.dataset.enterHousehold);if(switched&&loggedIn)showPage('overview');} }); $('#household-cover-file').onchange=async e=>{ const file=e.target.files[0];if(!file)return; const job=nextImageJob('cover'),generation=sessionGeneration,status=$('#household-cover-status'); imageJobs.get('cover').pending=true;status.dataset.error='false';status.textContent='正在压缩照片,请稍候…';$('#household-form .form-error').textContent=''; try{const image=await readImage(file,960);if(generation!==sessionGeneration||!imageJobCurrent('cover',job)||!$('#household-dialog').open)return; coverDraft=image;$('#household-cover-preview').src=image;$('#household-cover-preview').hidden=false;status.textContent='封面预览已就绪(约 '+Math.ceil((image.length-22)*.75/1024)+' KB),保存后上传百度网盘并更新卡片。'; }catch(error){if(generation===sessionGeneration&&imageJobCurrent('cover',job)){imageJobs.get('cover').error=true;status.dataset.error='true';status.textContent=error.message+'。本次图片未保存,请重新选择或取消本次选择。';}} finally{if(imageJobCurrent('cover',job))imageJobs.get('cover').pending=false;} }; $('#household-cover-cancel').onclick=()=>{ nextImageJob('cover');coverDraft=undefined;$('#household-cover-file').value=''; const item=(state.households||[]).find(x=>x.id===$('#household-form').elements.id.value),img=$('#household-cover-preview'); img.hidden=!item?.coverUrl;if(item?.coverUrl)img.src=item.coverUrl;else img.removeAttribute('src'); $('#household-cover-status').dataset.error='false';$('#household-cover-status').textContent='已取消本次图片选择,保留原有封面。';$('#household-form .form-error').textContent=''; }; $('#household-cover-reset').onclick=()=>{nextImageJob('cover');coverDraft='';$('#household-cover-file').value='';$('#household-cover-preview').hidden=true;$('#household-cover-preview').removeAttribute('src');$('#household-cover-status').dataset.error='false';$('#household-cover-status').textContent='保存后使用默认封面。';$('#household-form .form-error').textContent='';}; $('#household-dialog').addEventListener('close',()=>nextImageJob('cover')); $('#household-dialog').addEventListener('cancel',event=>{if(coverSaving)event.preventDefault();}); $('#household-cover-preview').addEventListener('error',()=>{$('#household-cover-status').textContent='封面暂时无法从网盘读取,请稍后重新打开;原有记录仍保留。';});