(function (root, factory) { // 家族圈动态模块同时支持浏览器页面和 Node 单元测试。 if (typeof module === 'object' && module.exports) { module.exports = factory(root); return; } root.FeedPages = factory(root); if (root.document) { root.document.addEventListener('DOMContentLoaded', function () { root.FeedPages.init(); }); } })(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { 'use strict'; var documentRef = root.document; function getApi() { return root.GenealogyApi && root.GenealogyApi.defaultClient; } function query(selector, rootNode) { return documentRef ? (rootNode || documentRef).querySelector(selector) : null; } function queryAll(selector, rootNode) { return documentRef ? Array.prototype.slice.call((rootNode || documentRef).querySelectorAll(selector)) : []; } function showMessage(message) { if (root.layui && root.layui.layer) { root.layui.layer.msg(message); return; } if (root.alert) root.alert(message); } function getQueryParam(search, name) { var params = new URLSearchParams(String(search || '').replace(/^\?/, '')); return params.get(name) || ''; } function getCurrentGenealogyId(search) { var page = query('[data-feed-page], [data-feed-edit-page]'); var source = search === undefined && root.location ? root.location.search : search; return getQueryParam(source, 'genealogyId') || (page && page.getAttribute('data-genealogy-id')) || ''; } function getCurrentFeedId(search) { var source = search === undefined && root.location ? root.location.search : search; return getQueryParam(source, 'feedId'); } function trimOrUndefined(value) { var text = String(value === undefined || value === null ? '' : value).trim(); return text || undefined; } function toNumberOrUndefined(value) { var text = trimOrUndefined(value); var number; if (!text) return undefined; number = Number(text); return Number.isFinite(number) ? number : undefined; } function buildFeedBody(values) { // FamilyFeedBody 只提交最新接口文档定义的字段。 var source = values || {}; var body = { feedType: trimOrUndefined(source.feedType) || 'text', feedContent: String(source.feedContent || '').trim(), mediaOssIds: trimOrUndefined(source.mediaOssIds), status: trimOrUndefined(source.status) }; var sortOrder = toNumberOrUndefined(source.sortOrder); if (sortOrder !== undefined) body.sortOrder = sortOrder; return body; } function buildCommentBody(values) { // FamilyFeedCommentBody 的 content 是唯一必填字段,其余字段按需携带。 var source = values || {}; var body = { content: String(source.content || '').trim() }; var parentCommentId = toNumberOrUndefined(source.parentCommentId); var replyUserId = toNumberOrUndefined(source.replyUserId); if (parentCommentId !== undefined) body.parentCommentId = parentCommentId; if (replyUserId !== undefined) body.replyUserId = replyUserId; return body; } function getFeedId(item) { var value = item && item.feedId; var text; if (value === undefined || value === null || value === '') return ''; text = String(value); return /^\d+$/.test(text) ? text : ''; } function getCommentId(item) { var value = item && item.commentId; var text; if (value === undefined || value === null || value === '') return ''; text = String(value); return /^\d+$/.test(text) ? text : ''; } function normalizeFeed(item) { var feedId = getFeedId(item); var feedContent = item && item.feedContent; var feed; if (!feedId || feedContent === undefined || feedContent === null) return null; feed = { feedId: feedId, feedContent: String(feedContent) }; if (trimOrUndefined(item.mediaOssIds)) feed.mediaOssIds = trimOrUndefined(item.mediaOssIds); if (trimOrUndefined(item.feedType)) feed.feedType = trimOrUndefined(item.feedType); if (trimOrUndefined(item.status)) feed.status = trimOrUndefined(item.status); return feed; } function normalizeComment(item) { var commentId = getCommentId(item); var content = item && item.content; if (!commentId || content === undefined || content === null) return null; return { commentId: commentId, content: String(content) }; } function normalizeList(data) { // AxiosRequestUtil 已解包 ListResult 与 PageResult,这里只接收对应的 data 或 rows。 if (Array.isArray(data)) return data; if (data && Array.isArray(data.rows)) return data.rows; return []; } function escapeHtml(value) { return String(value === undefined || value === null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function getListUrl(genealogyId, feedId) { var params = new URLSearchParams(); params.set('genealogyId', genealogyId); if (feedId) params.set('feedId', feedId); return 'profile-feed' + (feedId ? '-edit' : '') + '.html?' + params.toString(); } function renderFeedRow(feed, genealogyId) { return '
' + '

家族动态

' + escapeHtml(feed.feedContent) + '

' + '
' + '' + '' + '' + '' + '编辑' + '' + '
'; } function renderFeeds(data, genealogyId) { var container = query('[data-feed-list]'); var items = normalizeList(data).map(normalizeFeed); var invalidCount = items.filter(function (item) { return !item; }).length; var feeds = items.filter(Boolean); if (!container) return; if (invalidCount) { container.innerHTML = '
动态响应缺少 feedId 或 feedContent,请联系后端补充 DTO。
'; return; } if (!feeds.length) { container.innerHTML = '
暂无家族动态
'; return; } container.innerHTML = feeds.map(function (feed) { return renderFeedRow(feed, genealogyId); }).join(''); } function renderComments(feedId, data) { var row = query('[data-feed-id="' + feedId + '"]'); var comments = normalizeList(data).map(normalizeComment); var invalidCount = comments.filter(function (item) { return !item; }).length; var list = comments.filter(Boolean); var previous = row && row.nextElementSibling; if (!row) return; if (previous && previous.getAttribute('data-feed-comment-list') === feedId) previous.remove(); if (invalidCount) { row.insertAdjacentHTML('afterend', '
评论响应缺少 commentId 或 content,请联系后端补充 DTO。
'); return; } if (!list.length) { row.insertAdjacentHTML('afterend', '
暂无评论
'); return; } row.insertAdjacentHTML('afterend', '
' + list.map(function (comment) { return '

' + escapeHtml(comment.content) + '

'; }).join('') + '
'); } function getFormValues(form) { var values = {}; queryAll('[name]', form).forEach(function (field) { values[field.name] = field.value; }); return values; } function fillFeedForm(data) { var feed = normalizeFeed(data); if (!feed) throw new Error('动态响应缺少 feedId 或 feedContent,请联系后端补充 DTO。'); queryAll('[data-feed-form] [name]').forEach(function (field) { if (feed[field.name] !== undefined && feed[field.name] !== null) field.value = feed[field.name]; }); } function syncGenealogyLinks(genealogyId) { queryAll('[data-feed-create-link], [data-feed-list-link]').forEach(function (link) { link.href = getListUrl(genealogyId); }); } function requireGenealogyId() { var genealogyId = getCurrentGenealogyId(); var container; if (!genealogyId) { container = query('[data-feed-list]'); if (container) container.innerHTML = '
请从具体家谱进入家族圈动态
'; showMessage('请从具体家谱进入家族圈动态'); } return genealogyId; } async function loadFeeds() { var api = getApi(); var genealogyId = requireGenealogyId(); if (!api || !genealogyId) return; syncGenealogyLinks(genealogyId); try { renderFeeds(await api.feedsPage(genealogyId, { pageNum: 1, pageSize: 20 }), genealogyId); } catch (error) { showMessage(error.message || '家族动态加载失败'); } } async function loadFeedForEdit() { var api = getApi(); var genealogyId = requireGenealogyId(); var feedId = getCurrentFeedId(); if (!api || !genealogyId) return; syncGenealogyLinks(genealogyId); if (!feedId) return; try { fillFeedForm(await api.feedDetail(genealogyId, feedId)); } catch (error) { showMessage(error.message || '动态详情加载失败'); } } async function submitFeed(form) { var api = getApi(); var genealogyId = requireGenealogyId(); var feedId = getCurrentFeedId(); var body; if (!api || !genealogyId) return; // 富文本编辑器在提交前同步回 textarea,保证发送的是用户当前输入。 if (root.KindEditor && root.KindEditor.sync) root.KindEditor.sync('#feedContent'); body = buildFeedBody(getFormValues(form)); if (!body.feedContent) { showMessage('请填写动态内容'); return; } try { if (feedId) { await api.updateFeed(genealogyId, feedId, body); } else { await api.createFeed(genealogyId, body); } showMessage('动态已保存'); root.location.href = getListUrl(genealogyId); } catch (error) { showMessage(error.message || '动态保存失败'); } } async function likeFeed(feedId, shouldLike) { var api = getApi(); var genealogyId = requireGenealogyId(); if (!api || !genealogyId || !feedId) return; try { if (shouldLike) { await api.likeFeed(genealogyId, feedId); } else { await api.unlikeFeed(genealogyId, feedId); } await loadFeeds(); } catch (error) { showMessage(error.message || (shouldLike ? '点赞失败' : '取消点赞失败')); } } async function deleteFeed(feedId) { var api = getApi(); var genealogyId = requireGenealogyId(); if (!api || !genealogyId || !feedId) return; if (root.confirm && !root.confirm('确认删除这条动态吗?')) return; try { await api.deleteFeed(genealogyId, feedId); await loadFeeds(); } catch (error) { showMessage(error.message || '删除动态失败'); } } async function showComments(feedId) { var api = getApi(); var genealogyId = requireGenealogyId(); if (!api || !genealogyId || !feedId) return; try { renderComments(feedId, await api.feedComments(genealogyId, feedId)); } catch (error) { showMessage(error.message || '评论加载失败'); } } async function createComment(feedId) { var api = getApi(); var genealogyId = requireGenealogyId(); var content; var body; if (!api || !genealogyId || !feedId) return; content = root.prompt ? root.prompt('请输入评论内容', '') : ''; body = buildCommentBody({ content: content }); if (!body.content) return; try { await api.createFeedComment(genealogyId, feedId, body); await showComments(feedId); } catch (error) { showMessage(error.message || '评论发布失败'); } } async function deleteComment(feedId, commentId) { var api = getApi(); var genealogyId = requireGenealogyId(); if (!api || !genealogyId || !feedId || !commentId) return; if (root.confirm && !root.confirm('确认删除这条评论吗?')) return; try { await api.deleteFeedComment(genealogyId, feedId, commentId); await showComments(feedId); } catch (error) { showMessage(error.message || '删除评论失败'); } } function bindActions() { if (!documentRef) return; documentRef.addEventListener('submit', function (event) { var form = event.target.closest('[data-feed-form]'); if (!form) return; event.preventDefault(); submitFeed(form); }); documentRef.addEventListener('click', function (event) { var target = event.target; var feedId; var commentId; if (target.closest('[data-feed-like]')) { event.preventDefault(); likeFeed(target.closest('[data-feed-like]').getAttribute('data-feed-like'), true); } if (target.closest('[data-feed-unlike]')) { event.preventDefault(); likeFeed(target.closest('[data-feed-unlike]').getAttribute('data-feed-unlike'), false); } if (target.closest('[data-feed-comments]')) { event.preventDefault(); feedId = target.closest('[data-feed-comments]').getAttribute('data-feed-comments'); showComments(feedId); } if (target.closest('[data-feed-comment]')) { event.preventDefault(); feedId = target.closest('[data-feed-comment]').getAttribute('data-feed-comment'); createComment(feedId); } if (target.closest('[data-feed-delete]')) { event.preventDefault(); deleteFeed(target.closest('[data-feed-delete]').getAttribute('data-feed-delete')); } if (target.closest('[data-feed-comment-delete]')) { event.preventDefault(); commentId = target.closest('[data-feed-comment-delete]').getAttribute('data-feed-comment-delete'); feedId = target.closest('[data-feed-comment-delete]').getAttribute('data-feed-id'); deleteComment(feedId, commentId); } }); } function init() { if (!documentRef) return; bindActions(); if (query('[data-feed-page]')) loadFeeds(); if (query('[data-feed-edit-page]')) loadFeedForEdit(); } return { getCurrentGenealogyId: getCurrentGenealogyId, getCurrentFeedId: getCurrentFeedId, buildFeedBody: buildFeedBody, buildCommentBody: buildCommentBody, normalizeFeed: normalizeFeed, normalizeComment: normalizeComment, normalizeList: normalizeList, init: init }; });