' + escapeHtml(record.relativeName) + '
' + escapeHtml(summary.join(' · ')) + '
(function (root, factory) {
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.RelativePages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.RelativePages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
var recordsById = Object.create(null);
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 trimOrUndefined(value) {
var text = String(value === undefined || value === null ? '' : value).trim();
return text || undefined;
}
function queryParam(search, name) {
return new URLSearchParams(String(search || '').replace(/^\?/, '')).get(name) || '';
}
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 getCurrentGenealogyId(search) {
var source = search === undefined && root.location ? root.location.search : search;
var profileGenealogyId;
if (search === undefined && root.ProfileUI && root.ProfileUI.getGenealogyId) {
profileGenealogyId = root.ProfileUI.getGenealogyId();
if (profileGenealogyId) return normalizeId(profileGenealogyId);
}
return normalizeId(queryParam(source, 'genealogyId'));
}
function getCurrentRelativeId(search) {
var source = search === undefined && root.location ? root.location.search : search;
return normalizeId(queryParam(source, 'relativeId'));
}
function toFiniteNumber(value) {
var text = trimOrUndefined(value);
var number;
if (!text) return undefined;
number = Number(text);
return Number.isFinite(number) ? number : undefined;
}
function toSafeInteger(value) {
var number = toFiniteNumber(value);
return Number.isSafeInteger(number) ? number : undefined;
}
function decimalKey(value) {
var text = String(value).trim().replace(/^([+-]?)\./, '$10.');
var match = text.match(/^([+-]?)(\d+)(?:\.(\d*))?(?:[eE]([+-]?\d+))?$/);
var coefficient;
var scale;
if (!match) return '';
coefficient = BigInt((match[2] + (match[3] || '')).replace(/^0+(?=\d)/, ''));
scale = (match[3] || '').length - Number(match[4] || 0);
if (coefficient === 0n) return '0:0';
if (scale < 0) {
coefficient *= 10n ** BigInt(-scale);
scale = 0;
}
while (scale > 0 && coefficient % 10n === 0n) {
coefficient /= 10n;
scale -= 1;
}
if (match[1] === '-') coefficient = -coefficient;
return String(coefficient) + ':' + String(scale);
}
function isExactJsonNumber(sourceText, number) {
return Number.isFinite(number) && decimalKey(sourceText) === decimalKey(JSON.stringify(number));
}
function toBackendDateTime(value) {
var text = trimOrUndefined(value);
if (!text) return undefined;
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(text)) return text.replace('T', ' ') + ':00';
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/.test(text)) return text.replace('T', ' ');
return text;
}
function toDateTimeInputValue(value) {
var text = String(value || '');
if (!/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/.test(text)) return '';
return text.slice(0, 16).replace(' ', 'T');
}
function buildRelativeRecordBody(values) {
var source = values || {};
var body = { relativeName: String(source.relativeName || '').trim() };
var relationName = trimOrUndefined(source.relationName);
var eventName = trimOrUndefined(source.eventName);
var eventTime = toBackendDateTime(source.eventTime);
var amountText = trimOrUndefined(source.giftAmount);
var recordContent = trimOrUndefined(source.recordContent);
var mediaOssIds = trimOrUndefined(source.mediaOssIds);
var sortOrderText = trimOrUndefined(source.sortOrder);
var status = trimOrUndefined(source.status);
if (relationName !== undefined) body.relationName = relationName;
if (eventName !== undefined) body.eventName = eventName;
if (eventTime !== undefined) body.eventTime = eventTime;
if (amountText !== undefined) {
body.giftAmount = toFiniteNumber(amountText);
if (body.giftAmount === undefined) body.invalidGiftAmount = true;
else if (!isExactJsonNumber(amountText, body.giftAmount)) body.invalidGiftPrecision = true;
}
if (recordContent !== undefined) body.recordContent = recordContent;
if (mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds;
if (sortOrderText !== undefined) {
body.sortOrder = toSafeInteger(sortOrderText);
if (body.sortOrder === undefined) body.invalidSortOrder = true;
}
if (status !== undefined) body.status = status;
return body;
}
function validateRelativeRecordBody(body) {
if (!body || !body.relativeName) return '请填写亲友姓名';
if (body.invalidGiftAmount || (Object.prototype.hasOwnProperty.call(body, 'giftAmount') && !Number.isFinite(body.giftAmount))) {
return '礼金金额必须是有限数字';
}
if (body.invalidGiftPrecision) return '礼金金额超出浏览器可安全提交的精度';
if (body.eventTime !== undefined && !isValidBackendDateTime(body.eventTime)) {
return '事件时间格式无效';
}
if (body.mediaOssIds !== undefined && !/^[1-9][0-9]*(,[1-9][0-9]*)*$/.test(body.mediaOssIds)) {
return '附件上传结果无效';
}
if (body.invalidSortOrder || (Object.prototype.hasOwnProperty.call(body, 'sortOrder') && !Number.isSafeInteger(body.sortOrder))) {
return '排序值必须是安全整数';
}
if (body.status === '1') return '当前 PC 无法重新读取停用记录,暂不开放停用';
if (body.status !== undefined && body.status !== '0') return '记录状态只能是 0';
return '';
}
function isValidBackendDateTime(value) {
var match = String(value || '').match(/^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/);
var year;
var month;
var day;
var days;
if (!match) return false;
year = Number(match[1]);
month = Number(match[2]);
day = Number(match[3]);
if (month < 1 || month > 12 || Number(match[4]) > 23 || Number(match[5]) > 59 || Number(match[6]) > 59) return false;
days = [31, year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
return day >= 1 && day <= days[month - 1];
}
function stringValue(value) {
return value === undefined || value === null ? '' : String(value);
}
function normalizeAmount(value) {
var text;
if (value === undefined || value === null || value === '') return '';
if (typeof value === 'number') return Number.isFinite(value) ? String(value) : '';
text = String(value).trim();
return /^-?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/.test(text) ? text : '';
}
function optionalSafeInteger(value) {
var number;
if (value === undefined || value === null || value === '') return undefined;
number = Number(value);
return Number.isSafeInteger(number) ? number : undefined;
}
function normalizeRelativeRecord(item) {
var relativeId = normalizeId(item && item.relativeId);
var genealogyId = normalizeId(item && item.genealogyId);
var appUserId = normalizeId(item && item.appUserId);
var relativeName = String(item && item.relativeName || '').trim();
var status = stringValue(item && item.status);
var mediaOssIds = stringValue(item && item.mediaOssIds);
var giftAmount = normalizeAmount(item && item.giftAmount);
var sortOrder = optionalSafeInteger(item && item.sortOrder);
var record;
if (!relativeId || !genealogyId || !relativeName || (status !== '0' && status !== '1')) return null;
if (item.appUserId !== undefined && item.appUserId !== null && item.appUserId !== '' && !appUserId) return null;
if (item.giftAmount !== undefined && item.giftAmount !== null && item.giftAmount !== '' && giftAmount === '') return null;
if (mediaOssIds && !/^[1-9][0-9]*(,[1-9][0-9]*)*$/.test(mediaOssIds)) return null;
if (item.sortOrder !== undefined && item.sortOrder !== null && item.sortOrder !== '' && sortOrder === undefined) return null;
record = {
relativeId: relativeId,
genealogyId: genealogyId,
genealogyNo: stringValue(item.genealogyNo),
genealogyName: stringValue(item.genealogyName),
surname: stringValue(item.surname),
appUserNickName: stringValue(item.appUserNickName),
appUserPhone: stringValue(item.appUserPhone),
relativeName: relativeName,
relationName: stringValue(item.relationName),
eventName: stringValue(item.eventName),
eventTime: stringValue(item.eventTime),
giftAmount: giftAmount,
recordContent: stringValue(item.recordContent),
mediaOssIds: mediaOssIds,
status: status,
remark: stringValue(item.remark)
};
if (appUserId) record.appUserId = appUserId;
if (sortOrder !== undefined) record.sortOrder = sortOrder;
return record;
}
function matchesSavedRecord(item, expectedRelativeId) {
var record = normalizeRelativeRecord(item);
return Boolean(record && record.relativeId === normalizeId(expectedRelativeId));
}
function isEditableRecord(record) {
return Boolean(record && record.status === '0');
}
function escapeHtml(value) {
return stringValue(value)
.replace(/&/g, '&')
.replace(//g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function attachmentCount(mediaOssIds) {
return mediaOssIds ? mediaOssIds.split(',').filter(Boolean).length : 0;
}
function buildListUrl(genealogyId, relativeId) {
var url = 'profile-relative.html?genealogyId=' + encodeURIComponent(genealogyId);
return relativeId ? url + '&relativeId=' + encodeURIComponent(relativeId) : url;
}
function buildEditUrl(genealogyId, relativeId) {
var url = 'profile-relative-edit.html?genealogyId=' + encodeURIComponent(genealogyId);
return relativeId ? url + '&relativeId=' + encodeURIComponent(relativeId) : url;
}
function renderRelativeRow(record) {
var summary = [];
if (record.relationName) summary.push(record.relationName);
if (record.eventName) summary.push(record.eventName);
if (record.eventTime) summary.push(record.eventTime);
if (!summary.length) summary.push('未填写关系和事件');
return ' ' +
escapeHtml(summary.join(' · ')) + '' + escapeHtml(record.relativeName) + '
' + escapeHtml(label) + '' + escapeHtml(value || '未提供') + '
'; } function renderRelativeDetail(record) { var count; if (!record) return '