祀

家族纪念

MEMORIAL

家族纪念
FAMILY MEMORIAL

家族纪念

慎终追远 · 民德归厚
全球在线 — 人
向下浏览﹀
CONTENTS

内容索引

壹
先人纪念
生平 · 献花 · 追思
貳
家族世系
代际 · 血脉 · 传承
叁
const API = "https://family-api.843302042.workers.dev"; const SLUG = "linjia"; let DATA = {family:null, ancestors:[], tributes:[]}; function $(id){ return document.getElementById(id); } function esc(value){ return String(value == null ? "" : value).replace(/[&<>"']/g,function(c){ return {"&":"&","<":"<",">":">","\"":""","'":"'"}[c]; }); } async function loadData(){ const r = await fetch(API + "/api/data?family=" + SLUG); const d = await r.json(); if(!r.ok || d.error) throw new Error(d.error || "读取失败"); return d; } async function enter(){ const input = $("code"); const error = $("gateErr"); error.textContent = "正在验证…"; try{ const data = await loadData(); if(input.value.trim() !== data.family.access_code){ error.textContent = "邀请码不正确,请向家人确认。"; return; } DATA = data; $("gate").classList.add("hidden"); $("app").classList.remove("hidden"); if($("brandName")) $("brandName").textContent = data.family.name; if($("heroName")) $("heroName").textContent = data.family.name; if($("famName")) $("famName").textContent = data.family.name + " · 纪念"; renderAll(); }catch(errorObject){ console.error(errorObject); error.textContent = "读取失败,请检查网络或稍后重试。"; } } function renderAll(){ if($("viewHome")) $("viewHome").innerHTML = renderHome(); if($("viewTree")) $("viewTree").innerHTML = renderTree(); if($("viewGrave")) $("viewGrave").innerHTML = renderGrave(); if($("viewProxy")) $("viewProxy").innerHTML = renderProxy(); } function icon(kind){ if(kind === "flower") return "🌼"; if(kind === "candle") return "🕯"; return "✍"; } function renderHome(){ const list = DATA.ancestors || []; if(!list.length){ return '
还没有录入先人资料。
'; } return '
' + list.map(function(a){ const records = DATA.tributes.filter(function(t){ return t.ancestor_id === a.id; }); const count = {flower:0,candle:0,message:0}; records.forEach(function(t){ if(count[t.kind] !== undefined) count[t.kind]++; }); let html = '
'; html += '
' + esc((a.name || "?").charAt(0)) + '
'; html += '

' + esc(a.name) + '

'; html += '
'; html += esc(a.gender || "") + " · "; html += esc(a.birth_date || "?"); html += a.death_date ? " — " + esc(a.death_date) : "(健在)"; html += " · " + esc(a.native_place || ""); html += '
'; if(a.generation){ html += '
' + esc(a.generation) + '
'; } html += '

' + esc(a.biography || "") + '

'; if(a.grave_name){ html += '
'; html += '墓位:' + esc(a.grave_name) + '
'; html += esc(a.grave_note || "") + '
'; html += '一键导航前往 →'; html += '
'; } html += '
'; html += ''; html += ''; html += ''; html += '
'; records.slice(0,3).forEach(function(t){ html += '
' + icon(t.kind) + " " + esc(t.author || "访客") + ":" + esc(t.content || "") + '
'; }); html += '
'; return html; }).join("") + '
'; } function renderTree(){ const list = DATA.ancestors || []; if(!list.length){ return '
还没有录入先人资料。
'; } function node(person){ const children = list.filter(function(x){ return x.parent_id === person.id; }); let html = '
'; html += '' + esc(person.name) + ' '; html += '' + esc(person.generation || "") + ''; if(children.length){ html += '
'; html += children.map(node).join(""); html += '
'; } html += '
'; return html; } const roots = list.filter(function(a){ return !a.parent_id; }); return '
' + roots.map(node).join("") + '
'; } function renderGrave(){ const list = (DATA.ancestors || []).filter(function(a){ return a.grave_name; }); if(!list.length){ return '
还没有录入墓位信息。
'; } return '
' + list.map(function(a){ return '
' + '
' + esc((a.name || "?").charAt(0)) + '
' + '

' + esc(a.name) + '

' + '
' + esc(a.grave_name) + '
' + '
' + esc(a.grave_note || "位置待补充") + '
' + '打开地图导航 →' + '
'; }).join("") + '
'; } function renderProxy(){ let html = '
'; html += '

远在他乡或不便前往时,可登记代祭心愿。

'; html += ''; html += ''; html += ''; html += ''; html += ''; html += ''; html += '

'; html += '
'; return html; } async function tribute(ancestorId, kind){ let content = "献上一束花。"; if(kind === "candle"){ content = "点一支心灯。"; } if(kind === "message"){ content = prompt("写下您的追思:") || ""; if(!content) return; } try{ const response = await fetch(API + "/api/tribute",{ method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({ ancestor_id:ancestorId, kind:kind, author:"访客", content:content }) }); if(!response.ok) throw new Error("提交失败"); DATA = await loadData(); renderAll(); }catch(error){ alert("提交失败,请稍后再试。"); } } async function submitProxy(){ const name = $("proxyName").value.trim(); const message = $("proxyMessage"); if(!name){ message.textContent = "请先填写您的称呼。"; return; } try{ const response = await fetch(API + "/api/proxy",{ method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({ family_id:DATA.family.id, ancestor_id:$("proxyAncestor").value || null, contact:$("proxyContact").value.trim(), wish:$("proxyWish").value.trim(), preferred_date:$("proxyDate").value.trim() }) }); if(!response.ok) throw new Error("提交失败"); message.textContent = "已登记,我们会尽快联系您。"; }catch(error){ message.textContent = "提交失败,请稍后再试。"; } } document.addEventListener("DOMContentLoaded",function(){ const input = $("code"); const button = document.querySelector("#gate button"); if(button) button.onclick = enter; if(input){ input.addEventListener("keydown",function(event){ if(event.key === "Enter") enter(); }); } });