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 = '
';
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();
});
}
});