(function (root, factory) { // 家谱模块脚本同时支持浏览器运行和 Node 单元测试。 if (typeof module === 'object' && module.exports) { module.exports = factory(root); return; } root.GenealogyPages = factory(root); if (root.document) { root.document.addEventListener('DOMContentLoaded', function () { root.GenealogyPages.init(); }); } })(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { 'use strict'; var documentRef = root.document; function normalizeList(data) { // 列表接口可能返回数组、rows、records 或 list,这里统一成数组供页面渲染。 if (Array.isArray(data)) return data; if (!data || typeof data !== 'object') return []; if (Array.isArray(data.rows)) return data.rows; if (Array.isArray(data.records)) return data.records; if (Array.isArray(data.list)) return data.list; if (Array.isArray(data.data)) return data.data; return []; } function pick(item, keys, fallback) { // 后端字段名存在少量差异时,按优先级取第一个可用字段。 var source = item || {}; var index; var value; for (index = 0; index < keys.length; index += 1) { value = source[keys[index]]; if (value !== undefined && value !== null && value !== '') return value; } return fallback || ''; } function buildGenealogyBody(values) { // 创建家谱接口必填 genealogyName、surname、regionCode,地区编码由地区选择器回填。 var source = values || {}; return { genealogyName: source.genealogyName || '', surname: source.surname || '', regionCode: source.regionCode || '', addressDetail: source.addressDetail || '', intro: source.intro || '', visibility: source.visibility || '1', joinMode: source.joinMode || '1' }; } function getApi() { return root.GenealogyApi && root.GenealogyApi.defaultClient; } function query(selector, rootNode) { return (rootNode || documentRef).querySelector(selector); } function queryAll(selector, rootNode) { return Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)); } function readForm(form) { // 页面表单 name 与接口字段对齐,后续加字段只需要补 HTML。 var values = {}; queryAll('input[name], textarea[name], select[name]', form).forEach(function (field) { values[field.name] = String(field.value || '').trim(); }); return values; } function getQueryParam(search, name) { // 详情页和加入页用 query 参数传递 genealogyId,测试环境也可直接传 search 字符串。 var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); return params.get(name) || ''; } function getCurrentGenealogyId() { var search = root.location && root.location.search; return getQueryParam(search, 'id') || getQueryParam(search, 'genealogyId'); } function escapeHtml(value) { // 接口内容进入 innerHTML 前统一转义,避免公开页面渲染异常内容。 return String(value === undefined || value === null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function setBusy(button, busy) { // 加载状态只加 class,样式统一写在 CSS 中。 if (!button) return; button.classList.toggle('is-loading', busy); if ('disabled' in button) button.disabled = busy; button.setAttribute('aria-busy', busy ? 'true' : 'false'); } function showMessage(message) { if (root.layui && root.layui.layer) { root.layui.layer.msg(message); return; } root.alert(message); } function requireValue(value, message) { if (value) return true; showMessage(message); return false; } function buildFamilyHeroTitle(detail) { return pick(detail, ['genealogyName', 'name', 'title'], '未命名家谱'); } function formatJoinMode(value) { var map = { 0: '关闭加入', 1: '审核加入', 2: '邀请码加入' }; return map[value] || value || '多人共修'; } function buildFamilySummary(detail) { // 后端详情字段可能来自统计字段或普通展示字段,这里集中做字段映射。 var memberCount = pick(detail, ['memberCount', 'membersCount'], '0'); var generationCount = pick(detail, ['generationCount', 'generationTotal'], '0'); var articleCount = pick(detail, ['articleCount', 'articlesCount'], '0'); var albumCount = pick(detail, ['albumCount', 'albumsCount'], '0'); return [ { label: '成员', value: memberCount }, { label: '世代', value: generationCount }, { label: '谱文', value: articleCount }, { label: '相册', value: albumCount } ]; } function buildJoinApplyBody(values) { // 接口文档要求 applicantName、phone、relationDesc、applyReason,可选 inviterUserId。 var source = values || {}; var inviterUserId = source.inviterUserId ? Number(source.inviterUserId) : undefined; return { applicantName: String(source.applicantName || '').trim(), phone: String(source.phone || '').trim(), relationDesc: String(source.relationDesc || '').trim(), applyReason: String(source.applyReason || '').trim() || '申请加入家谱', inviterUserId: inviterUserId }; } function renderPublicGenealogies(items) { // 广场页复用现有卡片结构,只替换接口返回的数据。 var container = query('[data-genealogy-list="public"]'); var html; if (!container) return; if (!items.length) { container.innerHTML = '
暂无公开家谱
'; return; } html = items.slice(0, 6).map(function (item, index) { // 第一条沿用推荐卡片样式,其余走普通家谱卡片样式。 var id = pick(item, ['genealogyId', 'id'], ''); var name = pick(item, ['genealogyName', 'name', 'title'], '未命名家谱'); var surname = pick(item, ['surname'], '家谱'); var region = pick(item, ['addressDetail', 'regionName', 'originPlace'], '地区待完善'); var intro = pick(item, ['intro', 'description', 'summary'], '家谱资料正在完善中。'); var className = index === 0 ? 'family-card featured tilt-card' : 'card family-lite tilt-card'; return '' + '

' + name + '

' + '

' + intro + '

' + '
' + region + '' + surname + '
' + '
'; }).join(''); container.innerHTML = html; } function buildMyGenealogyRow(item) { // 我的家谱列表保持 profile-module.css 的 module-row 结构。 var id = pick(item, ['genealogyId', 'id'], ''); var name = pick(item, ['genealogyName', 'name'], '未命名家谱'); var role = pick(item, ['roleName', 'memberRole'], '成员'); var count = pick(item, ['memberCount', 'membersCount'], '0'); return '' + '

' + name + '

' + role + ' · 共修入 ' + count + ' 人

' + '进入主页
'; } function renderMyGenealogies(items) { // 没有数据时显示公共空状态,避免保留旧静态示例误导用户。 var container = query('[data-genealogy-list="mine"]'); if (!container) return; if (!items.length) { container.innerHTML = '
你还没有创建或加入家谱
'; return; } container.innerHTML = items.map(buildMyGenealogyRow).join(''); } function renderFamilyDetail(detail) { var page = query('[data-family-detail]'); var summaryGrid = query('[data-family-summary]'); var joinLink = query('[data-family-join-link]'); var id; if (!page) return; id = pick(detail, ['genealogyId', 'id'], getCurrentGenealogyId()); queryAll('[data-family-text]').forEach(function (node) { var field = node.getAttribute('data-family-text'); var fallback = node.getAttribute('data-fallback') || ''; var value = field === 'title' ? buildFamilyHeroTitle(detail) : pick(detail, field.split('|'), fallback); node.textContent = field.indexOf('joinMode') !== -1 ? formatJoinMode(value) : value; }); if (summaryGrid) { summaryGrid.innerHTML = buildFamilySummary(detail).map(function (item) { return '' + escapeHtml(item.value) + '' + escapeHtml(item.label) + ''; }).join(''); } if (joinLink && id) { joinLink.href = 'join-genealogy.html?id=' + encodeURIComponent(id); } } async function loadFamilyDetail() { // 只有详情页存在 data-family-detail,且 URL 带 id/genealogyId 时才请求接口。 var api = getApi(); var page = query('[data-family-detail]'); var genealogyId = getCurrentGenealogyId(); if (!api || !page) return; if (!genealogyId) { showMessage('缺少家谱ID'); return; } page.classList.add('is-loading'); try { renderFamilyDetail(await api.genealogyDetail(genealogyId)); } catch (error) { showMessage(error.message || '家谱详情加载失败'); } finally { page.classList.remove('is-loading'); } } async function submitJoinApply(form, button) { var api = getApi(); var values = readForm(form); var genealogyId = values.genealogyId || getCurrentGenealogyId(); var body = buildJoinApplyBody(values); if (!api) return; if (!requireValue(genealogyId, '请填写家谱ID或邀请码')) return; if (!requireValue(body.applicantName, '请填写你的姓名')) return; if (!requireValue(body.phone, '请填写手机号')) return; if (!requireValue(body.relationDesc, '请填写关系说明')) return; setBusy(button, true); try { await api.applyJoinGenealogy(genealogyId, body); showMessage('申请已提交,请等待管理员审核'); root.location.href = form.getAttribute('data-success-url') || 'profile-join-family.html'; } catch (error) { showMessage(error.message || '申请提交失败'); } finally { setBusy(button, false); } } async function loadPublicGenealogies() { // 只有带 data-genealogy-list="public" 的页面才会真正发起请求。 var api = getApi(); var container = query('[data-genealogy-list="public"]'); if (!api || !container) return; container.classList.add('is-loading'); try { renderPublicGenealogies(normalizeList(await api.publicGenealogies())); } catch (error) { showMessage(error.message || '公开家谱加载失败'); } finally { container.classList.remove('is-loading'); } } async function loadMyGenealogies() { // 只有个人中心“我的家谱”页面会命中这个容器。 var api = getApi(); var container = query('[data-genealogy-list="mine"]'); if (!api || !container) return; container.classList.add('is-loading'); try { renderMyGenealogies(normalizeList(await api.myGenealogies())); } catch (error) { showMessage(error.message || '我的家谱加载失败'); } finally { container.classList.remove('is-loading'); } } async function submitGenealogy(form, button) { // 创建成功后跳到我的家谱列表,后续继续维护成员和内容。 var api = getApi(); var body = buildGenealogyBody(readForm(form)); if (!api) return; if (!requireValue(body.genealogyName, '请填写家谱名称')) return; if (!requireValue(body.surname, '请填写主要姓氏')) return; if (!requireValue(body.regionCode, '请选择家族所在地')) return; setBusy(button, true); try { await api.createGenealogy(body); showMessage('家谱创建成功'); root.location.href = form.getAttribute('data-success-url') || 'profile-families.html'; } catch (error) { showMessage(error.message || '家谱创建失败'); } finally { setBusy(button, false); } } function bindCreateForm() { // 创建表单用 submit 事件,兼容按钮点击和键盘回车提交。 var form = query('[data-genealogy-form="create"]'); if (!form) return; form.addEventListener('submit', function (event) { event.preventDefault(); submitGenealogy(form, query('[type="submit"]', form)); }); } function bindJoinForm() { // 加入页用同一个 submit 入口,兼容详情页带 id 跳转和用户手动输入家谱ID。 var form = query('[data-genealogy-form="join"]'); var idField; var currentId; if (!form) return; idField = query('[name="genealogyId"]', form); currentId = getCurrentGenealogyId(); if (idField && currentId && !idField.value) idField.value = currentId; form.addEventListener('submit', function (event) { event.preventDefault(); submitJoinApply(form, query('[type="submit"]', form)); }); } function init() { // 一个模块脚本服务多个家谱页面,通过 data 属性决定当前页面要做什么。 if (!documentRef) return; bindCreateForm(); bindJoinForm(); loadPublicGenealogies(); loadMyGenealogies(); loadFamilyDetail(); } return { normalizeList: normalizeList, pick: pick, buildGenealogyBody: buildGenealogyBody, getQueryParam: getQueryParam, buildFamilyHeroTitle: buildFamilyHeroTitle, buildFamilySummary: buildFamilySummary, formatJoinMode: formatJoinMode, buildJoinApplyBody: buildJoinApplyBody, buildMyGenealogyRow: buildMyGenealogyRow, init: init }; });