Files
jiapu/public/js/generation-pages.js
T
2026-07-09 17:30:10 +08:00

350 lines
10 KiB
JavaScript

(function (root, factory) {
// 字辈谱模块同时支持浏览器页面和 Node 单元测试。
if (typeof module === 'object' && module.exports) {
module.exports = factory(root);
return;
}
root.GenerationPages = factory(root);
if (root.document) {
root.document.addEventListener('DOMContentLoaded', function () {
root.GenerationPages.init();
});
}
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
'use strict';
var documentRef = root.document;
function normalizeList(data) {
// 通用列表响应可能是数组,也可能包在 rows、records、list 或 data 中。
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 escapeHtml(value) {
// 字辈文本进入 innerHTML 前统一转义。
return String(value === undefined || value === null ? '' : value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function getApi() {
return root.GenealogyApi && root.GenealogyApi.defaultClient;
}
function query(selector, rootNode) {
if (!documentRef) return null;
return (rootNode || documentRef).querySelector(selector);
}
function queryAll(selector, rootNode) {
if (!documentRef) return [];
return 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() {
var search = root.location && root.location.search;
var page = query('[data-generation-page]');
return getQueryParam(search, 'id') ||
getQueryParam(search, 'genealogyId') ||
(page && page.getAttribute('data-genealogy-id')) ||
'';
}
function toNumber(value, fallback) {
var number = Number(String(value || '').trim());
return Number.isFinite(number) && number > 0 ? number : fallback;
}
function toBoolean(value) {
return value === true || value === 'true' || value === '1' || value === 'on';
}
function buildGenerationBody(values) {
// GenerationPoemBody 必填 generationNo、generationText,其余字段给接口默认值。
var source = values || {};
var generationNo = toNumber(source.generationNo, 1);
return {
generationNo: generationNo,
generationText: String(source.generationText || '').trim(),
description: String(source.description || '').trim(),
sortOrder: toNumber(source.sortOrder, generationNo),
status: String(source.status || '').trim() || '0'
};
}
function buildGenerationBatchBody(values) {
// GenerationPoemBatchBody 只包含批量文本和是否遇到断代即停止。
var source = values || {};
return {
content: String(source.content || '').trim(),
stopMissingOldGeneration: toBoolean(source.stopMissingOldGeneration)
};
}
function getFormValues(form) {
var values = {};
queryAll('[name]', form).forEach(function (field) {
if (field.type === 'checkbox') {
values[field.name] = field.checked ? 'on' : '';
return;
}
values[field.name] = field.value;
});
return values;
}
function setBatchStatus(message) {
var statusNode = query('[data-generation-batch-status]');
if (statusNode) {
statusNode.textContent = message || '';
}
}
function buildGenerationRow(item, genealogyId) {
var poemId = pick(item, ['poemId', 'id'], '');
var generationNo = pick(item, ['generationNo', 'generation', 'sortOrder'], '1');
var text = pick(item, ['generationText', 'content', 'text'], '未设置');
var memberCount = pick(item, ['memberCount', 'personCount', 'count'], '0');
var description = pick(item, ['description', 'remark'], '暂无备注');
return '<div class="module-row generation-row" data-generation-id="' + escapeHtml(poemId) + '">' +
'<div><h3>第 ' + escapeHtml(generationNo) + ' 代:' + escapeHtml(text) + '</h3><p>' + escapeHtml(memberCount) + ' 人 · ' + escapeHtml(description) + '</p></div>' +
'<button class="pill" type="button" data-generation-edit="' + escapeHtml(poemId) + '" data-generation-no="' + escapeHtml(generationNo) + '" data-generation-text="' + escapeHtml(text) + '" data-genealogy-id="' + escapeHtml(genealogyId) + '">编辑</button>' +
'</div>';
}
function renderGenerations(items, genealogyId) {
var container = query('[data-generation-list]');
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">暂无字辈记录</div>';
return;
}
container.innerHTML = items.map(function (item) {
return buildGenerationRow(item, genealogyId);
}).join('');
}
function renderBatchPreview(data) {
var container = query('[data-generation-batch-preview]');
var items = normalizeList(data);
if (!container) return;
if (!items.length) {
container.innerHTML = '<div class="api-empty">预览完成,接口未返回明细</div>';
return;
}
container.innerHTML = items.map(function (item) {
return buildGenerationRow(item, getCurrentGenealogyId());
}).join('');
}
async function loadGenerations() {
var api = getApi();
var container = query('[data-generation-list]');
var genealogyId = getCurrentGenealogyId();
if (!api || !container) return;
if (!genealogyId) {
container.innerHTML = '<div class="api-empty">请从具体家谱进入字辈谱</div>';
return;
}
container.classList.add('is-loading');
try {
renderGenerations(normalizeList(await api.generationPoems(genealogyId)), genealogyId);
} catch (error) {
showMessage(error.message || '字辈谱加载失败');
} finally {
container.classList.remove('is-loading');
}
}
async function createGeneration() {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var generationNo;
var generationText;
var body;
if (!api || !genealogyId) {
showMessage('请从具体家谱进入字辈谱');
return;
}
generationNo = root.prompt('请输入世代序号', '');
if (!generationNo) return;
generationText = root.prompt('请输入字辈内容', '');
if (!generationText) return;
body = buildGenerationBody({
generationNo: generationNo,
generationText: generationText
});
try {
await api.createGenerationPoem(genealogyId, body);
showMessage('字辈已新增');
loadGenerations();
} catch (error) {
showMessage(error.message || '新增字辈失败');
}
}
async function editGeneration(button) {
var api = getApi();
var genealogyId = button.getAttribute('data-genealogy-id') || getCurrentGenealogyId();
var poemId = button.getAttribute('data-generation-edit');
var currentText = button.getAttribute('data-generation-text') || '';
var generationNo = button.getAttribute('data-generation-no') || '1';
var nextText;
if (!api || !genealogyId || !poemId) return;
nextText = root.prompt('请输入新的字辈', currentText);
if (!nextText) return;
button.classList.add('is-loading');
try {
await api.updateGenerationPoem(genealogyId, poemId, buildGenerationBody({
generationNo: generationNo,
generationText: nextText
}));
showMessage('字辈已更新');
loadGenerations();
} catch (error) {
showMessage(error.message || '更新字辈失败');
} finally {
button.classList.remove('is-loading');
}
}
async function submitGenerationBatch(action) {
var api = getApi();
var genealogyId = getCurrentGenealogyId();
var form = query('[data-generation-batch-form]');
var body;
var result;
if (!api || !genealogyId || !form) {
showMessage('请从具体家谱进入字辈谱');
return;
}
body = buildGenerationBatchBody(getFormValues(form));
if (!body.content) {
showMessage('请填写批量字辈内容');
return;
}
try {
setBatchStatus(action === 'save' ? '批量保存中...' : '批量预览中...');
if (action === 'save') {
await api.saveGenerationPoemsBatch(genealogyId, body);
setBatchStatus('批量保存完成');
showMessage('批量字辈已保存');
loadGenerations();
return;
}
result = await api.previewGenerationPoems(genealogyId, body);
renderBatchPreview(result);
setBatchStatus('批量预览完成');
} catch (error) {
setBatchStatus('批量处理失败');
showMessage(error.message || '批量字辈处理失败');
}
}
function bindActions() {
if (!documentRef) return;
documentRef.addEventListener('click', function (event) {
var addButton = event.target.closest('[data-generation-add]');
var editButton = event.target.closest('[data-generation-edit]');
var batchButton = event.target.closest('[data-generation-batch-action]');
if (addButton) {
event.preventDefault();
createGeneration();
}
if (editButton) {
event.preventDefault();
editGeneration(editButton);
}
if (batchButton) {
event.preventDefault();
submitGenerationBatch(batchButton.getAttribute('data-generation-batch-action'));
}
});
}
function init() {
if (!documentRef) return;
bindActions();
loadGenerations();
}
return {
normalizeList: normalizeList,
buildGenerationBody: buildGenerationBody,
buildGenerationBatchBody: buildGenerationBatchBody,
buildGenerationRow: buildGenerationRow,
init: init
};
});