286 lines
10 KiB
JavaScript
286 lines
10 KiB
JavaScript
(function (root, factory) {
|
|
if (typeof module === 'object' && module.exports) {
|
|
module.exports = factory(root);
|
|
return;
|
|
}
|
|
|
|
root.FamilyHomePages = factory(root);
|
|
if (root.document) {
|
|
root.document.addEventListener('DOMContentLoaded', function () {
|
|
root.FamilyHomePages.init();
|
|
});
|
|
}
|
|
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
|
'use strict';
|
|
|
|
var MediaDisplay = root.MediaDisplay || (typeof require === 'function' ? require('./media-display.js') : null);
|
|
|
|
function escapeHtml(value) {
|
|
return String(value === undefined || value === null ? '' : value)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function normalizeId(value) {
|
|
var result;
|
|
|
|
if (typeof value === 'number' && !Number.isSafeInteger(value)) return '';
|
|
if (value === undefined || value === null) return '';
|
|
result = String(value).trim();
|
|
return /^[1-9][0-9]*$/.test(result) ? result : '';
|
|
}
|
|
|
|
function text(value) {
|
|
if (value === undefined || value === null) return '';
|
|
return String(value).trim();
|
|
}
|
|
|
|
function normalizeCount(value) {
|
|
var result = Number(value);
|
|
|
|
return Number.isSafeInteger(result) && result >= 0 ? result : null;
|
|
}
|
|
|
|
function normalizeFamilyOverview(item, expectedGenealogyId) {
|
|
var source = item || {};
|
|
var genealogyId = normalizeId(source.genealogyId);
|
|
var expectedId = normalizeId(expectedGenealogyId);
|
|
var memberCount = normalizeCount(source.memberCount);
|
|
var personCount = normalizeCount(source.personCount);
|
|
var coverFile = MediaDisplay && MediaDisplay.normalizeFileAccess(source.coverFile);
|
|
var overview;
|
|
|
|
if (!genealogyId || genealogyId !== expectedId ||
|
|
!text(source.genealogyName) || String(source.status) !== '0' ||
|
|
memberCount === null || personCount === null) {
|
|
return null;
|
|
}
|
|
overview = {
|
|
genealogyId: genealogyId,
|
|
genealogyNo: text(source.genealogyNo),
|
|
genealogyName: text(source.genealogyName),
|
|
surname: text(source.surname),
|
|
ancestralHall: text(source.ancestralHall),
|
|
originPlace: text(source.originPlace),
|
|
regionFullName: text(source.regionFullName),
|
|
memberCount: memberCount,
|
|
personCount: personCount,
|
|
canManage: source.canManage === true,
|
|
canEditContent: source.canEditContent === true
|
|
};
|
|
if (source.coverFile !== undefined && source.coverFile !== null && !coverFile) return null;
|
|
if (coverFile) overview.coverFile = coverFile;
|
|
return overview;
|
|
}
|
|
|
|
function renderFamilyOverview(overview) {
|
|
var source = overview || {};
|
|
var details = [
|
|
source.genealogyNo ? '家谱编号 ' + source.genealogyNo : '',
|
|
source.surname ? '姓氏 ' + source.surname : '',
|
|
source.ancestralHall ? '堂号 ' + source.ancestralHall : '',
|
|
source.originPlace ? '祖籍 ' + source.originPlace : '',
|
|
source.regionFullName ? '地区 ' + source.regionFullName : ''
|
|
].filter(Boolean).join(' · ');
|
|
|
|
return (MediaDisplay ? MediaDisplay.renderImage(source.coverFile, {
|
|
alt: source.genealogyName + '封面',
|
|
className: 'family-cover-media'
|
|
}) : '') + '<h3>' + escapeHtml(source.genealogyName) + '</h3><p>' +
|
|
escapeHtml(details) + '</p><p>成员 ' + escapeHtml(source.memberCount) +
|
|
' 人 · 世系人物 ' + escapeHtml(source.personCount) + ' 人</p>';
|
|
}
|
|
|
|
function shouldShowFamilyManagement(overview) {
|
|
return !!overview && overview.canManage === true;
|
|
}
|
|
|
|
function shouldRedirectToLogin(api, error) {
|
|
var status = error && (error.status || error.code);
|
|
|
|
return !api || !api.getToken || !api.getToken() || Number(status) === 401;
|
|
}
|
|
|
|
function isForbidden(error) {
|
|
return Number(error && (error.status || error.code)) === 403;
|
|
}
|
|
|
|
async function loadFamilyHome(api, genealogyId) {
|
|
var normalizedId = normalizeId(genealogyId);
|
|
var results;
|
|
var overview;
|
|
|
|
if (!normalizedId || !api || !api.genealogyOverview || !api.lineageTree) {
|
|
throw new Error('家谱主页接口初始化失败');
|
|
}
|
|
results = await Promise.all([
|
|
api.genealogyOverview(normalizedId),
|
|
api.lineageTree(normalizedId)
|
|
]);
|
|
overview = normalizeFamilyOverview(results[0], normalizedId);
|
|
if (!overview) throw new Error('家谱概览响应无效');
|
|
return {
|
|
overview: overview,
|
|
tree: results[1]
|
|
};
|
|
}
|
|
|
|
function redirectToLogin(api) {
|
|
if (api && api.clearToken) api.clearToken();
|
|
if (!root.location) return;
|
|
if (typeof root.location.replace === 'function') root.location.replace('login.html');
|
|
else root.location.href = 'login.html';
|
|
}
|
|
|
|
function query(selector) {
|
|
return root.document && root.document.querySelector(selector);
|
|
}
|
|
|
|
function queryAll(selector) {
|
|
return root.document ? Array.prototype.slice.call(root.document.querySelectorAll(selector)) : [];
|
|
}
|
|
|
|
function setText(selector, value) {
|
|
var element = query(selector);
|
|
|
|
if (element) element.textContent = value;
|
|
}
|
|
|
|
function setHomeState(selector, type, message) {
|
|
var element = query(selector);
|
|
|
|
if (!element) return;
|
|
if (root.ProfileUI && root.ProfileUI.setApiState) {
|
|
root.ProfileUI.setApiState(element, type, message);
|
|
return;
|
|
}
|
|
element.textContent = message;
|
|
}
|
|
|
|
function setHomeStatus(message, type) {
|
|
var element = query('[data-family-home-status]');
|
|
|
|
if (!element) return;
|
|
element.hidden = !message;
|
|
if (!message) {
|
|
element.innerHTML = '';
|
|
return;
|
|
}
|
|
if (type && root.ProfileUI && root.ProfileUI.setApiState) {
|
|
root.ProfileUI.setApiState(element, type, message);
|
|
return;
|
|
}
|
|
element.textContent = message;
|
|
}
|
|
|
|
async function initFamilyHomePage() {
|
|
var page = query('[data-family-home-page]');
|
|
var api = root.GenealogyApi && root.GenealogyApi.defaultClient;
|
|
var genealogyId;
|
|
var result;
|
|
var treeHtml;
|
|
|
|
if (!page) return;
|
|
if (shouldRedirectToLogin(api)) {
|
|
redirectToLogin(api);
|
|
return;
|
|
}
|
|
genealogyId = root.ProfileUI && root.ProfileUI.getGenealogyId();
|
|
if (!genealogyId) {
|
|
if (root.location && typeof root.location.replace === 'function') {
|
|
root.location.replace('profile-families.html?next=profile-family-home.html');
|
|
}
|
|
return;
|
|
}
|
|
if (root.ProfileUI && root.ProfileUI.syncGenealogyContextLinks) {
|
|
root.ProfileUI.syncGenealogyContextLinks();
|
|
}
|
|
if (!root.LineagePages || !root.LineagePages.renderLineageTreeHtml) {
|
|
setHomeState('[data-family-home-overview]', 'error', '家谱主页初始化失败,请刷新后重试。');
|
|
setHomeState('[data-lineage-home-tree]', 'error', '世系预览初始化失败,请刷新后重试。');
|
|
setHomeStatus('世系预览初始化失败,请刷新后重试。', 'error');
|
|
return;
|
|
}
|
|
setHomeState('[data-family-home-overview]', 'loading', '正在加载家谱概览…');
|
|
setHomeState('[data-lineage-home-tree]', 'loading', '正在加载世系树…');
|
|
setHomeStatus('正在读取家谱概览…', 'loading');
|
|
try {
|
|
result = normalizeFamilyOverview(await api.genealogyOverview(genealogyId), genealogyId);
|
|
if (!result) throw new Error('家谱概览响应无效');
|
|
if (root.ProfileUI && root.ProfileUI.setCurrentGenealogyContext) {
|
|
root.ProfileUI.setCurrentGenealogyContext(result.genealogyId, result.genealogyName);
|
|
}
|
|
setText('[data-family-home-title]', result.genealogyName);
|
|
setText('[data-family-home-context]', result.genealogyName);
|
|
query('[data-family-home-overview]').innerHTML = renderFamilyOverview(result);
|
|
setText('[data-family-home-member-count]', String(result.memberCount));
|
|
setText('[data-family-home-person-count]', String(result.personCount));
|
|
queryAll('[data-family-home-management]').forEach(function (element) {
|
|
element.hidden = !shouldShowFamilyManagement(result);
|
|
});
|
|
} catch (error) {
|
|
if (shouldRedirectToLogin(api, error)) {
|
|
redirectToLogin(api);
|
|
return;
|
|
}
|
|
setHomeState(
|
|
'[data-family-home-overview]',
|
|
isForbidden(error) ? 'forbidden' : 'error',
|
|
isForbidden(error) ? '当前账号无权查看该家谱。' : '家谱概览读取失败,请稍后重试。'
|
|
);
|
|
setHomeState(
|
|
'[data-lineage-home-tree]',
|
|
isForbidden(error) ? 'forbidden' : 'error',
|
|
isForbidden(error) ? '当前账号无权查看该家谱。' : '世系树读取失败,请稍后重试。'
|
|
);
|
|
setHomeStatus(
|
|
isForbidden(error) ? '当前账号无权查看该家谱。' : (error.message || '家谱主页读取失败,请稍后重试。'),
|
|
isForbidden(error) ? 'forbidden' : 'error'
|
|
);
|
|
return;
|
|
}
|
|
try {
|
|
result = await api.lineageTree(genealogyId);
|
|
treeHtml = Array.isArray(result) ? root.LineagePages.renderLineageTreeHtml(result) : '';
|
|
if (!treeHtml) {
|
|
setHomeState('[data-lineage-home-tree]', 'empty', '暂无世系树');
|
|
} else {
|
|
query('[data-lineage-home-tree]').innerHTML = treeHtml;
|
|
}
|
|
setHomeStatus('');
|
|
} catch (error) {
|
|
if (shouldRedirectToLogin(api, error)) {
|
|
redirectToLogin(api);
|
|
return;
|
|
}
|
|
setHomeState(
|
|
'[data-lineage-home-tree]',
|
|
isForbidden(error) ? 'forbidden' : 'error',
|
|
isForbidden(error) ? '当前账号无权查看该家谱世系树。' : '世系树读取失败,请稍后重试。'
|
|
);
|
|
setHomeStatus(
|
|
isForbidden(error) ? '当前账号无权查看该家谱世系树。' : (error.message || '世系树读取失败,请稍后重试。'),
|
|
isForbidden(error) ? 'forbidden' : 'error'
|
|
);
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
return initFamilyHomePage();
|
|
}
|
|
|
|
return {
|
|
normalizeFamilyOverview: normalizeFamilyOverview,
|
|
renderFamilyOverview: renderFamilyOverview,
|
|
shouldShowFamilyManagement: shouldShowFamilyManagement,
|
|
shouldRedirectToLogin: shouldRedirectToLogin,
|
|
isForbidden: isForbidden,
|
|
loadFamilyHome: loadFamilyHome,
|
|
initFamilyHomePage: initFamilyHomePage,
|
|
init: init
|
|
};
|
|
});
|