63 lines
6.8 KiB
JavaScript
63 lines
6.8 KiB
JavaScript
|
|
'use strict';
|
||
|
|
const historyFields={name:'姓名',alias:'别名',gender:'性别',lifeStatus:'在世情况',birthDate:'出生日期',deathDate:'去世日期',birthplace:'籍贯',biography:'生平',note:'备注',archived:'归档',fromId:'关系一方',toId:'关系另一方',kind:'关系类型',lineage:'亲子属性',active:'有效关系',title:'记事标题',eventDate:'发生日期',summary:'摘要',body:'正文',personIds:'关联人物',status:'发布状态',sourceName:'资料来源',sourceUrl:'报道链接'};
|
||
|
|
let historyContext=null,historySelection=null,historyGeneration=0,historyDetailGeneration=0;
|
||
|
|
function clearFamilyHistory(){historyGeneration++;historyDetailGeneration++;historyContext=null;historySelection=null;for(const id of ['history-list','history-detail','history-error'])$('#'+id).replaceChildren();}
|
||
|
|
function historyValue(key,value){
|
||
|
|
if(value===undefined||value===null||value==='')return '未填写';
|
||
|
|
if(key==='personIds')return value.length?value.map(personName).join('、'):'全家';
|
||
|
|
if(key==='fromId'||key==='toId')return personName(value);
|
||
|
|
if(typeof value==='boolean')return value?'是':'否';
|
||
|
|
const labels={male:'男',female:'女',unknown:'待确认',alive:'在世',deceased:'已故',draft:'草稿',published:'已发布',parent:'父母 → 子女',spouse:'配偶',biological:'亲生',adoptive:'收养',step:'继亲',unspecified:'未注明'};
|
||
|
|
return ['gender','lifeStatus','status','kind','lineage'].includes(key)?labels[value]||String(value):String(value);
|
||
|
|
}
|
||
|
|
async function openFamilyHistory(kind,id){
|
||
|
|
if(!family.canEdit)return;clearFamilyHistory();
|
||
|
|
if($('#event-detail-dialog').open)$('#event-detail-dialog').close();
|
||
|
|
historyContext={kind,id,revision:family.revision,nextBefore:null};
|
||
|
|
$('#history-title').textContent='修改记录 · '+({person:personName(id),link:'亲属关系',event:family.events.find(e=>e.id===id)?.title||'家族记事'})[kind];
|
||
|
|
$('#history-dialog').showModal();await loadFamilyHistory();
|
||
|
|
}
|
||
|
|
async function loadFamilyHistory(more=false){
|
||
|
|
if(!historyContext)return;const context=historyContext,generation=++historyGeneration,b=$('#history-more');b.disabled=true;
|
||
|
|
try{const result=await api('/api/family/history?'+new URLSearchParams({kind:context.kind,id:context.id,...(more&&context.nextBefore?{before:context.nextBefore}:{})}));
|
||
|
|
if(!loggedIn||generation!==historyGeneration||context!==historyContext)return;
|
||
|
|
context.nextBefore=result.nextBefore;if(!more)context.revision=result.revision;
|
||
|
|
const html=result.items.map(c=>`<button class="history-entry quiet" data-history-change="${c.revision}"><strong>版本 ${c.revision} · ${c.restoredFrom?'恢复旧版':c.created?'创建记录':'修改记录'}</strong><span>${esc(c.actor)} · ${esc(new Date(c.at).toLocaleString())}</span><small>${esc(c.fields.map(f=>historyFields[f]||f).join('、')||'内容未变化')}</small></button>`).join('');
|
||
|
|
if(more)$('#history-list').insertAdjacentHTML('beforeend',html);else $('#history-list').innerHTML=html||'<p class="description">暂无修改记录。此功能从本次升级开始记录;升级前的内容会在下一次修改时保留。</p>';
|
||
|
|
b.hidden=!context.nextBefore;$('#history-error').textContent='';
|
||
|
|
}catch(e){if(!e.stale&&generation===historyGeneration)$('#history-error').textContent=e.message;}finally{if(generation===historyGeneration)b.disabled=false;}
|
||
|
|
}
|
||
|
|
async function showFamilyChange(revision){
|
||
|
|
const context=historyContext,generation=++historyDetailGeneration;historySelection=null;$('#history-detail').textContent='正在读取版本…';
|
||
|
|
try{const c=await api('/api/family/change?revision='+revision);
|
||
|
|
if(!loggedIn||generation!==historyDetailGeneration||context!==historyContext)return;
|
||
|
|
historySelection=c;
|
||
|
|
const keys=Object.keys(historyFields).filter(k=>k in c.after||k in (c.before||{}));
|
||
|
|
$('#history-detail').innerHTML=`<h3>版本 ${c.revision} 的内容对照</h3><p class="description">恢复只更新这条记录。已发生的后续修改仍可在历史中查阅。</p><div class="history-columns">${['before','after'].map(side=>`<section><h4>${side==='before'?'本次修改前':'本次修改后'}</h4>${c[side]?`<dl>${keys.map(k=>`<div class="${JSON.stringify(c.before?.[k])!==JSON.stringify(c.after[k])?'history-changed':''}"><dt>${esc(historyFields[k])}</dt><dd>${esc(historyValue(k,c[side][k]))}</dd></div>`).join('')}</dl><button class="secondary" data-history-prepare="${side}">选择恢复${side==='before'?'修改前':'此版本'}</button>`:'<p>创建前没有记录。</p>'}</section>`).join('')}</div><div id="history-confirm" hidden></div>`;
|
||
|
|
$('#history-error').textContent='';
|
||
|
|
}catch(e){if(!e.stale&&generation===historyDetailGeneration)$('#history-detail').textContent=e.message;}
|
||
|
|
}
|
||
|
|
function prepareFamilyRestore(side){
|
||
|
|
const c=historySelection;if(!c?.[side])return;const value=c[side],box=$('#history-confirm');
|
||
|
|
box.hidden=false;box.innerHTML=`<p>将用版本 ${c.revision} ${side==='before'?'修改前':'修改后'}的内容替换这条记录的当前内容。${c.kind==='event'?'恢复后记事状态:'+esc(eventStatus(value))+'。':''}</p><button data-history-restore="${side}">确认恢复并生成新版本</button><button class="quiet" id="history-cancel-restore">取消恢复</button>`;box.scrollIntoView({block:'nearest'});
|
||
|
|
}
|
||
|
|
async function restoreFamilyChange(side){
|
||
|
|
const context=historyContext,c=historySelection;if(!context||!c?.[side])return;
|
||
|
|
const generation=historyDetailGeneration;$$('#history-confirm button').forEach(b=>b.disabled=true);
|
||
|
|
try{await api('/api/family/restore',{changeRevision:c.revision,side,revision:context.revision});
|
||
|
|
if(!loggedIn||context!==historyContext)return;
|
||
|
|
historyDetailGeneration++;historySelection=null;$('#history-detail').replaceChildren();
|
||
|
|
await loadFamilyHistory();const loaded=await loadFamily();if(loggedIn)toast(loaded?'已恢复,原有版本仍保留在修改记录中':'已恢复,读取最新家谱失败,请重新载入');
|
||
|
|
}catch(e){if(!e.stale&&context===historyContext){$('#history-error').textContent=e.message;if(e.status===409)$('#history-error').append(document.createTextNode('。请关闭并重新打开修改记录,核对当前内容后再选择恢复。'));}}
|
||
|
|
finally{if(generation===historyDetailGeneration)$$('#history-confirm button').forEach(b=>b.disabled=false);}
|
||
|
|
}
|
||
|
|
$('#history-more').onclick=()=>loadFamilyHistory(true);
|
||
|
|
$('#history-dialog').addEventListener('close',clearFamilyHistory);
|
||
|
|
document.addEventListener('click',e=>{const b=e.target.closest('button');if(!b)return;
|
||
|
|
if(b.dataset.historyKind)openFamilyHistory(b.dataset.historyKind,b.dataset.historyId);
|
||
|
|
if(b.dataset.historyChange)showFamilyChange(Number(b.dataset.historyChange));
|
||
|
|
if(b.dataset.historyPrepare)prepareFamilyRestore(b.dataset.historyPrepare);
|
||
|
|
if(b.dataset.historyRestore)restoreFamilyChange(b.dataset.historyRestore);
|
||
|
|
if(b.id==='history-cancel-restore')$('#history-confirm').hidden=true;
|
||
|
|
});
|