(function (root, factory) { if (typeof module === 'object' && module.exports) { module.exports = factory(root); return; } root.NotificationPages = factory(root); if (root.document) { root.document.addEventListener('DOMContentLoaded', function () { root.NotificationPages.init(); }); } })(typeof globalThis !== 'undefined' ? globalThis : window, function (root) { 'use strict'; var documentRef = root.document; var notificationsById = Object.create(null); var selectedNotificationId = ''; var writePending = false; function getApi() { return root.GenealogyApi && root.GenealogyApi.defaultClient; } function query(selector, node) { return documentRef ? (node || documentRef).querySelector(selector) : null; } function queryAll(selector, node) { return documentRef ? Array.prototype.slice.call((node || documentRef).querySelectorAll(selector)) : []; } function normalizeId(value) { if (value === undefined || value === null || value === '') return ''; if (typeof value === 'number' && !Number.isSafeInteger(value)) return ''; return /^[1-9][0-9]*$/.test(String(value)) ? String(value) : ''; } function normalizeNullableId(value) { var id; if (value === undefined || value === null || value === '') return null; id = normalizeId(value); return id || undefined; } function stringValue(value) { return value === undefined || value === null ? '' : String(value); } function buildNotificationQuery(readStatus) { return readStatus === '0' || readStatus === '1' ? { readStatus: readStatus } : {}; } function normalizeNotification(item) { var notificationId = normalizeId(item && item.notificationId); var genealogyId = normalizeNullableId(item && item.genealogyId); var senderUserId = normalizeNullableId(item && item.senderUserId); var bizId = normalizeNullableId(item && item.bizId); var readStatus = stringValue(item && item.readStatus); var status = stringValue(item && item.status); if (!notificationId || genealogyId === undefined || senderUserId === undefined || bizId === undefined) return null; if ((readStatus !== '0' && readStatus !== '1') || (status !== '0' && status !== '1')) return null; return { notificationId: notificationId, genealogyId: genealogyId, genealogyNo: stringValue(item.genealogyNo), genealogyName: stringValue(item.genealogyName), senderUserId: senderUserId, senderNickName: stringValue(item.senderNickName), senderPhone: stringValue(item.senderPhone), noticeType: stringValue(item.noticeType), noticeTitle: stringValue(item.noticeTitle), noticeContent: stringValue(item.noticeContent), bizType: stringValue(item.bizType), bizId: bizId, bizSummary: stringValue(item.bizSummary), publishTime: stringValue(item.publishTime), readStatus: readStatus, readTime: item.readTime === undefined || item.readTime === null ? null : String(item.readTime), status: status, remark: stringValue(item.remark) }; } function normalizeNotifications(data) { var normalized; if (!Array.isArray(data)) return []; normalized = data.map(normalizeNotification); return normalized.some(function (item) { return !item; }) ? [] : normalized; } function getUnreadCount(data) { var count = Number(data); return Number.isSafeInteger(count) && count >= 0 ? count : 0; } function matchesNotification(item, expectedNotificationId) { var notification = normalizeNotification(item); return Boolean(notification && notification.notificationId === normalizeId(expectedNotificationId)); } function canMarkRead(notification) { return Boolean(notification && notification.status === '0' && notification.readStatus === '0'); } function escapeHtml(value) { return stringValue(value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function renderNotificationRow(notification) { var statusText = notification.readStatus === '0' ? '未读' : '已读'; var action = canMarkRead(notification) ? '' : ''; return '
' + '

' + escapeHtml(notification.noticeTitle || '通知') + '

' + escapeHtml(notification.noticeContent || '暂无通知内容') + '

' + escapeHtml([notification.publishTime, statusText].filter(Boolean).join(' · ')) + '

' + action + '
'; } function detailValue(label, value) { return '

' + escapeHtml(label) + '' + escapeHtml(value || '未提供') + '

'; } function renderNotificationDetail(notification) { var action; if (!notification) return '
请选择一条通知查看详情
'; action = canMarkRead(notification) ? '
' : ''; return '
' + detailValue('通知标题', notification.noticeTitle) + detailValue('通知内容', notification.noticeContent) + detailValue('通知类型', notification.noticeType) + detailValue('家谱', notification.genealogyName) + detailValue('发送人', notification.senderNickName) + detailValue('业务类型', notification.bizType) + detailValue('业务摘要', notification.bizSummary) + detailValue('发布时间', notification.publishTime) + detailValue('阅读状态', notification.readStatus === '0' ? '未读' : '已读') + detailValue('阅读时间', notification.readTime) + detailValue('备注', notification.remark) + '
' + action; } function showMessage(message) { if (root.layui && root.layui.layer) root.layui.layer.msg(message); else if (root.alert) root.alert(message); } function shouldRedirectToLogin(api, error) { var status = error && (error.status || error.code); return !api || !api.getToken || !api.getToken() || Number(status) === 401; } function redirectUnauthorized(api, error) { if (!shouldRedirectToLogin(api, error)) return false; if (api && api.clearToken) api.clearToken(); root.NavigationUtil.open('login.html'); return true; } function isForbidden(error) { return Number(error && (error.status || error.code)) === 403; } function setNotificationState(target, type, message) { if (!target) return; if (root.ProfileUI && root.ProfileUI.setApiState && type) { root.ProfileUI.setApiState(target, type, message); return; } target.textContent = message || ''; } function updateStatus(type, message) { var status = query('[data-notification-status]'); setNotificationState(status, type, message); } function setWritePending(value) { writePending = Boolean(value); queryAll('[data-notification-read-id], [data-notification-read-all], [data-notification-refresh], [data-notification-filter]').forEach(function (control) { control.disabled = writePending; }); } function renderNotifications(data) { var container = query('[data-notification-list]'); var normalized = Array.isArray(data) ? data.map(normalizeNotification) : []; var invalidCount = normalized.filter(function (item) { return !item; }).length; var notifications = normalized.filter(Boolean); notificationsById = Object.create(null); notifications.forEach(function (notification) { notificationsById[notification.notificationId] = notification; }); if (!container) return notifications; if (!Array.isArray(data) || invalidCount) { setNotificationState(container, 'error', '通知列表响应无效,请稍后重试。'); return []; } if (!notifications.length) { setNotificationState(container, 'empty', '当前暂无通知。'); return []; } container.innerHTML = notifications.map(renderNotificationRow).join(''); if (writePending) setWritePending(true); return notifications; } function renderDetail(notification) { var container = query('[data-notification-detail]'); if (!container) return; if (!notification) { setNotificationState(container, 'empty', '请选择一条通知查看详情。'); return; } container.innerHTML = renderNotificationDetail(notification); } async function loadNotificationDetail(notificationId) { var api = getApi(); var notification; if (!normalizeId(notificationId) || redirectUnauthorized(api)) return null; setNotificationState(query('[data-notification-detail]'), 'loading', '正在读取通知详情…'); try { notification = normalizeNotification(await api.notificationDetail(notificationId)); if (!notification || !matchesNotification(notification, notificationId)) { throw new Error('通知详情未返回同一条稳定 NotificationView'); } selectedNotificationId = notification.notificationId; notificationsById[notification.notificationId] = notification; renderDetail(notification); return notification; } catch (error) { if (redirectUnauthorized(api, error)) return null; setNotificationState(query('[data-notification-detail]'), isForbidden(error) ? 'forbidden' : 'error', isForbidden(error) ? '暂无权限查看该通知详情。' : '通知详情加载失败,请稍后重试。'); updateStatus(isForbidden(error) ? 'forbidden' : 'error', isForbidden(error) ? '暂无权限查看该通知详情。' : (error.message || '通知详情加载失败,请稍后重试。')); return null; } } function currentFilter() { var filter = query('[data-notification-filter]'); return filter ? filter.value : ''; } async function loadNotifications() { var api = getApi(); var countTarget = query('[data-notification-unread-count]'); var result; var notifications; var nextSelectedId; if (redirectUnauthorized(api)) return; if (!api.notifications || !api.notificationDetail || !api.unreadNotificationCount) { setNotificationState(query('[data-notification-list]'), 'error', '消息服务初始化失败,请刷新后重试。'); updateStatus('error', '消息服务初始化失败,请刷新后重试。'); return; } setNotificationState(query('[data-notification-list]'), 'loading', '正在同步消息…'); updateStatus('loading', '正在同步消息…'); try { result = await Promise.all([ api.notifications(buildNotificationQuery(currentFilter())), api.unreadNotificationCount() ]); notifications = renderNotifications(result[0]); if (countTarget) countTarget.textContent = String(getUnreadCount(result[1])); nextSelectedId = selectedNotificationId && notificationsById[selectedNotificationId] ? selectedNotificationId : (notifications[0] && notifications[0].notificationId); if (nextSelectedId) await loadNotificationDetail(nextSelectedId); else renderDetail(null); updateStatus('', '消息已同步'); } catch (error) { if (redirectUnauthorized(api, error)) return; renderNotifications(null); setNotificationState(query('[data-notification-detail]'), isForbidden(error) ? 'forbidden' : 'error', isForbidden(error) ? '暂无权限查看通知详情。' : '消息同步失败,请稍后重试。'); updateStatus(isForbidden(error) ? 'forbidden' : 'error', isForbidden(error) ? '暂无权限查看通知列表。' : (error.message || '消息同步失败,请稍后重试。')); } } async function markNotificationRead(notificationId) { var api = getApi(); var notification = notificationsById[notificationId]; if (writePending || !normalizeId(notificationId) || !canMarkRead(notification) || redirectUnauthorized(api)) return; setWritePending(true); updateStatus('loading', '正在标记通知为已读…'); try { await api.markNotificationRead(notificationId); selectedNotificationId = notificationId; showMessage('通知已标记为已读'); await loadNotifications(); } catch (error) { if (redirectUnauthorized(api, error)) return; updateStatus(isForbidden(error) ? 'forbidden' : 'error', isForbidden(error) ? '暂无权限标记该通知为已读。' : (error.message || '标记通知已读失败,请稍后重试。')); } finally { setWritePending(false); } } async function markAllRead() { var api = getApi(); if (writePending || redirectUnauthorized(api)) return; if (!api.markAllNotificationsRead) { updateStatus('error', '消息服务初始化失败,请刷新后重试。'); return; } setWritePending(true); updateStatus('loading', '正在将全部通知标记为已读…'); try { await api.markAllNotificationsRead(); showMessage('已全部标记为已读'); await loadNotifications(); } catch (error) { if (redirectUnauthorized(api, error)) return; updateStatus(isForbidden(error) ? 'forbidden' : 'error', isForbidden(error) ? '暂无权限标记全部通知为已读。' : (error.message || '全部标记已读失败,请稍后重试。')); } finally { setWritePending(false); } } function bindActions() { if (!documentRef) return; documentRef.addEventListener('click', function (event) { var detailButton = event.target.closest('[data-notification-detail-id]'); var readButton = event.target.closest('[data-notification-read-id]'); if (detailButton) { event.preventDefault(); loadNotificationDetail(detailButton.getAttribute('data-notification-detail-id')); return; } if (readButton) { event.preventDefault(); markNotificationRead(readButton.getAttribute('data-notification-read-id')); } }); if (query('[data-notification-read-all]')) { query('[data-notification-read-all]').addEventListener('click', markAllRead); } if (query('[data-notification-refresh]')) { query('[data-notification-refresh]').addEventListener('click', loadNotifications); } if (query('[data-notification-filter]')) { query('[data-notification-filter]').addEventListener('change', function () { selectedNotificationId = ''; loadNotifications(); }); } } function init() { if (!documentRef || !query('[data-notification-list]')) return; bindActions(); loadNotifications(); } return { buildNotificationQuery: buildNotificationQuery, normalizeNotification: normalizeNotification, normalizeNotifications: normalizeNotifications, getUnreadCount: getUnreadCount, matchesNotification: matchesNotification, canMarkRead: canMarkRead, renderNotificationRow: renderNotificationRow, renderNotificationDetail: renderNotificationDetail, shouldRedirectToLogin: shouldRedirectToLogin, isForbidden: isForbidden, loadNotifications: loadNotifications, init: init }; });