(function (root, factory) { if (typeof module === 'object' && module.exports) { module.exports = factory(root); return; } root.GenealogyEntryPages = factory(root); if (root.document) { root.document.addEventListener('DOMContentLoaded', function () { root.GenealogyEntryPages.init(); }); } })(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { 'use strict'; function normalizeTargetPage(value) { var target = String(value || ''); var hashIndex = target.indexOf('#'); var hash = hashIndex < 0 ? '' : target.slice(hashIndex); var parts; var params; if (hashIndex >= 0) target = target.slice(0, hashIndex); parts = target.split('?'); if (!/^profile-[a-z-]+\.html$/.test(parts[0])) return 'profile-family-home.html'; params = new URLSearchParams(parts[1] || ''); params.delete('genealogyId'); return parts[0] + (params.toString() ? '?' + params.toString() : '') + hash; } function getTargetPage(search) { var params = new URLSearchParams(String(search === undefined && root.location ? root.location.search : search || '').replace(/^\?/, '')); return normalizeTargetPage(params.get('next')); } function buildStatus(quota) { var source = quota || {}; var details = []; if (Number(source.createRemaining) === -1) details.push('创建不限'); else if (Number(source.createRemaining) >= 0) details.push('还可创建 ' + source.createRemaining + ' 部'); if (Number(source.joinRemaining) === -1) details.push('加入不限'); else if (Number(source.joinRemaining) >= 0) details.push('还可加入 ' + source.joinRemaining + ' 部'); return details.length ? '家谱额度:' + details.join(',') + '。' : ''; } function escapeHtml(value) { return String(value === undefined || value === null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function trimOrUndefined(value) { var text; if (value === undefined || value === null) return undefined; text = String(value).trim(); return text || undefined; } function normalizeId(value) { var text; if (typeof value === 'number' && !Number.isSafeInteger(value)) return ''; if (value === undefined || value === null) return ''; text = String(value).trim(); return /^[1-9][0-9]*$/.test(text) ? text : ''; } function buildGenealogyCreateBody(values) { var source = values || {}; var body = { genealogyName: String(source.genealogyName || '').trim(), surname: String(source.surname || '').trim(), regionCode: String(source.regionCode || '').trim() }; var optionalNames = ['ancestralHall', 'originPlace', 'addressDetail', 'intro']; var coverOssId = normalizeId(source.coverOssId); var visibility = trimOrUndefined(source.visibility); var joinMode = trimOrUndefined(source.joinMode); optionalNames.forEach(function (name) { var value = trimOrUndefined(source[name]); if (value !== undefined) body[name] = value; }); if (source.coverOssId !== undefined && source.coverOssId !== null && source.coverOssId !== '') { if (coverOssId) body.coverOssId = coverOssId; else body.invalidCoverOssId = true; } if (visibility !== undefined) body.visibility = visibility; if (joinMode !== undefined) body.joinMode = joinMode; return body; } function validateGenealogyCreateBody(body) { if (!body || !body.genealogyName) return '请填写谱名'; if (!body.surname) return '请填写姓氏'; if (!body.regionCode) return '请选择家谱所在地区'; if (body.visibility !== undefined && ['0', '1', '2'].indexOf(body.visibility) < 0) return '可见范围选项无效'; if (body.joinMode !== undefined && ['0', '1', '2'].indexOf(body.joinMode) < 0) return '加入方式选项无效'; if (body.invalidCoverOssId) return '封面文件无效,请重新选择'; return ''; } function canCreateGenealogy(quota) { var remaining = Number(quota && quota.createRemaining); if (quota && typeof quota.canCreate === 'boolean') return quota.canCreate; return Number.isInteger(remaining) && (remaining === -1 || remaining > 0); } function normalizeCreatedGenealogy(item) { var genealogyId = normalizeId(item && item.genealogyId); var genealogyName = String(item && item.genealogyName || '').trim(); var surname = String(item && item.surname || '').trim(); if (!genealogyId || !genealogyName || !surname) return null; return { genealogyId: genealogyId, genealogyName: genealogyName, surname: surname, regionFullName: String(item && item.regionFullName || '') }; } function buildCreatedGenealogyUrl(item) { var genealogy = normalizeCreatedGenealogy(item); return genealogy ? buildEntryUrl(genealogy.genealogyId, 'profile-family-home.html') : ''; } function shouldRedirectToLogin(api, error) { var status = error && (error.status || error.code); return !api || !api.getToken || !api.getToken() || Number(status) === 401; } function redirectToLogin(api) { if (api && api.clearToken) api.clearToken(); root.NavigationUtil.open('login.html'); } function getFormValues(form) { var values = {}; Array.prototype.forEach.call(form.querySelectorAll('[name]'), function (field) { values[field.name] = field.value; }); return values; } function getApiStateType(error) { return Number(error && (error.status || error.code)) === 403 ? 'forbidden' : 'error'; } function setCreateStatus(message, type) { var status = root.document && root.document.querySelector('[data-genealogy-create-status]'); if (!status) return; if (!message) { status.innerHTML = ''; return; } if (root.ProfileUI && root.ProfileUI.setApiState) { root.ProfileUI.setApiState(status, type || 'error', message); return; } status.textContent = message; } function setEntryStatus(message, type) { var status = root.document && root.document.querySelector('[data-genealogy-entry-status]'); if (!status) return; if (!message) { status.innerHTML = ''; return; } if (type && root.ProfileUI && root.ProfileUI.setApiState) { root.ProfileUI.setApiState(status, type, message); return; } status.textContent = message; } function setGenealogyListState(container, type, message) { if (!container) return; if (root.ProfileUI && root.ProfileUI.setApiState) { root.ProfileUI.setApiState(container, type, message); return; } container.textContent = message; } function setCreatePending(pending) { var button = root.document && root.document.querySelector('[data-genealogy-create-submit]'); if (!button) return; if (!button.dataset.defaultLabel) button.dataset.defaultLabel = button.textContent; button.disabled = Boolean(pending); button.textContent = pending ? '正在创建…' : button.dataset.defaultLabel; button.setAttribute('aria-busy', pending ? 'true' : 'false'); } async function initGenealogyCreatePage() { var form = root.document && root.document.querySelector('[data-genealogy-create-form]'); var quotaElement = root.document && root.document.querySelector('[data-genealogy-create-quota]'); var api = root.GenealogyApi && root.GenealogyApi.defaultClient; var quota; var writePending = false; if (!form) return; if (shouldRedirectToLogin(api)) { redirectToLogin(api); return; } setCreateStatus('正在读取创建额度…', 'loading'); try { quota = await api.genealogyQuota(); if (quotaElement) quotaElement.textContent = buildStatus(quota) || '当前额度信息不可用'; if (!canCreateGenealogy(quota)) { setCreateStatus('当前没有可用的家谱创建额度', 'empty'); form.querySelector('[data-genealogy-create-submit]').disabled = true; return; } setCreateStatus(''); } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } if (quotaElement) quotaElement.textContent = '家谱额度读取失败'; setCreateStatus(error.message || '无法读取家谱创建额度', getApiStateType(error)); form.querySelector('[data-genealogy-create-submit]').disabled = true; return; } if (form.__genealogyCreateBound) return; form.__genealogyCreateBound = true; form.addEventListener('submit', async function (event) { var body; var validation; var created; var verified; event.preventDefault(); if (writePending) return; body = buildGenealogyCreateBody(getFormValues(form)); validation = validateGenealogyCreateBody(body); if (validation) { setCreateStatus(validation, 'error'); return; } delete body.invalidCoverOssId; writePending = true; setCreatePending(true); setCreateStatus('正在创建家谱…', 'loading'); try { created = normalizeCreatedGenealogy(await api.createGenealogy(body)); if (!created) throw new Error('创建响应缺少有效家谱信息'); verified = normalizeCreatedGenealogy(await api.genealogyDetail(created.genealogyId)); if (!verified || verified.genealogyId !== created.genealogyId) throw new Error('创建后无法读取同一家谱'); if (root.ProfileUI && root.ProfileUI.setCurrentGenealogyContext) { root.ProfileUI.setCurrentGenealogyContext(verified.genealogyId, verified.genealogyName); } root.NavigationUtil.open(buildCreatedGenealogyUrl(verified)); } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } setCreateStatus(error.message || '创建家谱失败,请稍后重试', getApiStateType(error)); } finally { writePending = false; setCreatePending(false); } }); } function normalizeGenealogy(item) { var source = item || {}; var id = source.genealogyId === undefined || source.genealogyId === null ? '' : String(source.genealogyId); if (!/^[1-9][0-9]*$/.test(id) || !source.genealogyName) return null; return { genealogyId: id, genealogyName: String(source.genealogyName), surname: source.surname === undefined || source.surname === null ? '' : String(source.surname), regionFullName: source.regionFullName === undefined || source.regionFullName === null ? '' : String(source.regionFullName), memberCount: source.memberCount, personCount: source.personCount, roleType: source.roleType === undefined || source.roleType === null ? '' : String(source.roleType), canManage: source.canManage === true }; } function buildEntryUrl(genealogyId, target) { var normalizedTarget = normalizeTargetPage(target); var parts = normalizedTarget.split('#'); var pathAndQuery = parts[0]; var hash = parts.length > 1 ? '#' + parts.slice(1).join('#') : ''; var pathParts = pathAndQuery.split('?'); var params = new URLSearchParams(pathParts[1] || ''); params.set('genealogyId', String(genealogyId)); return pathParts[0] + '?' + params.toString() + hash; } function renderGenealogies(container, data, target) { var items = Array.isArray(data) ? data.map(normalizeGenealogy).filter(Boolean) : []; if (!container) return items; if (!Array.isArray(data) || (data.length && !items.length)) { setGenealogyListState(container, 'error', '家谱响应字段不完整,请联系后端核对。'); return []; } if (!items.length) { setGenealogyListState(container, 'empty', '当前账号还没有可进入的家谱'); return []; } container.innerHTML = items.map(function (item) { var meta = [ item.surname ? item.surname + '氏' : '', item.regionFullName, item.memberCount === undefined || item.memberCount === null ? '' : item.memberCount + ' 位成员', item.personCount === undefined || item.personCount === null ? '' : item.personCount + ' 位世系人物' ].filter(Boolean).join(' · '); return '' + '

' + escapeHtml(item.genealogyName) + '

' + escapeHtml(meta) + '

' + (item.canManage ? '管理并进入' : '进入家谱') + '
'; }).join(''); return items; } async function init() { if (root.document && root.document.querySelector('[data-genealogy-create-page]')) { await initGenealogyCreatePage(); return; } var status = root.document && root.document.querySelector('[data-genealogy-entry-status]'); var api = root.GenealogyApi && root.GenealogyApi.defaultClient; var target = getTargetPage(); var container = root.document && root.document.querySelector('[data-genealogy-list="mine"]'); var sortSelect = root.document && root.document.querySelector('[data-genealogy-sort-select]'); var sortUp = root.document && root.document.querySelector('[data-genealogy-sort-up]'); var sortDown = root.document && root.document.querySelector('[data-genealogy-sort-down]'); var sortSave = root.document && root.document.querySelector('[data-genealogy-sort-save]'); var result; var sortPending = false; if (!status) return; if (shouldRedirectToLogin(api)) { redirectToLogin(api); return; } if (!api || !api.genealogyQuota || !api.genealogiesMine || !api.saveGenealogyOrder) { setEntryStatus('家谱入口接口初始化失败,请刷新后重试。', 'error'); setGenealogyListState(container, 'error', '家谱列表接口初始化失败,请刷新后重试。'); return; } setGenealogyListState(container, 'loading', '正在读取我的家谱…'); setEntryStatus('正在读取家谱额度…', 'loading'); try { result = await api.genealogiesMine(); renderGenealogies(container, result, target); if (sortSelect) { var sortItems = Array.isArray(result) ? result.map(normalizeGenealogy).filter(Boolean) : []; sortSelect.innerHTML = sortItems.map(function (item) { return ''; }).join(''); [sortSelect, sortUp, sortDown, sortSave].forEach(function (control) { if (control) control.disabled = sortItems.length < 2; }); } } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } setGenealogyListState( container, getApiStateType(error), getApiStateType(error) === 'forbidden' ? '当前账号无权查看我的家谱。' : '无法读取我的家谱,请稍后重试。' ); } function moveSelected(offset) { var index; var targetIndex; var option; if (!sortSelect || sortPending) return; index = sortSelect.selectedIndex; targetIndex = index + offset; if (index < 0 || targetIndex < 0 || targetIndex >= sortSelect.options.length) return; option = sortSelect.options[index]; if (offset < 0) sortSelect.insertBefore(option, sortSelect.options[targetIndex]); else sortSelect.insertBefore(sortSelect.options[targetIndex], option); sortSelect.selectedIndex = targetIndex; } if (sortUp) sortUp.addEventListener('click', function () { moveSelected(-1); }); if (sortDown) sortDown.addEventListener('click', function () { moveSelected(1); }); if (sortSave) sortSave.addEventListener('click', async function () { if (sortPending || !sortSelect) return; sortPending = true; [sortSelect, sortUp, sortDown, sortSave].forEach(function (control) { if (control) control.disabled = true; }); setEntryStatus('正在保存家谱顺序…', 'loading'); try { await api.saveGenealogyOrder(Array.prototype.map.call(sortSelect.options, function (option) { return option.value; })); setEntryStatus('家谱顺序已保存'); } catch (error) { setEntryStatus(error.message || '家谱顺序保存失败,请稍后重试。', getApiStateType(error)); } finally { sortPending = false; [sortSelect, sortUp, sortDown, sortSave].forEach(function (control) { if (control) control.disabled = sortSelect.options.length < 2; }); } }); try { result = await api.genealogyQuota(); setEntryStatus(buildStatus(result)); } catch (error) { if (shouldRedirectToLogin(api, error)) { redirectToLogin(api); return; } setEntryStatus( getApiStateType(error) === 'forbidden' ? '当前账号无权读取家谱额度。' : '家谱额度读取失败,请稍后重试。', getApiStateType(error) ); } } return { normalizeTargetPage: normalizeTargetPage, getTargetPage: getTargetPage, buildStatus: buildStatus, buildGenealogyCreateBody: buildGenealogyCreateBody, validateGenealogyCreateBody: validateGenealogyCreateBody, canCreateGenealogy: canCreateGenealogy, normalizeCreatedGenealogy: normalizeCreatedGenealogy, buildCreatedGenealogyUrl: buildCreatedGenealogyUrl, shouldRedirectToLogin: shouldRedirectToLogin, initGenealogyCreatePage: initGenealogyCreatePage, normalizeGenealogy: normalizeGenealogy, buildEntryUrl: buildEntryUrl, renderGenealogies: renderGenealogies, init: init }; });