631 lines
24 KiB
JavaScript
631 lines
24 KiB
JavaScript
(function (root, factory) {
|
|
if (typeof module === 'object' && module.exports) {
|
|
module.exports = factory(root);
|
|
return;
|
|
}
|
|
|
|
root.ArticlePages = factory(root);
|
|
if (root.document) {
|
|
root.document.addEventListener('DOMContentLoaded', function () {
|
|
root.ArticlePages.init();
|
|
});
|
|
}
|
|
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
|
'use strict';
|
|
|
|
function confirmAction(message) {
|
|
if (root.ProfileUI && typeof root.ProfileUI.confirmAction === 'function') {
|
|
return root.ProfileUI.confirmAction(message);
|
|
}
|
|
return Promise.resolve(!root['confirm'] || root['confirm'](message));
|
|
}
|
|
|
|
var MediaDisplay = root.MediaDisplay || (typeof require === 'function' ? require('./media-display.js') : null);
|
|
|
|
var documentRef = root.document;
|
|
var articlesById = Object.create(null);
|
|
var writePending = false;
|
|
var canEditContent = 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 stringValue(value) {
|
|
return value === undefined || value === null ? '' : String(value);
|
|
}
|
|
|
|
function trimOrUndefined(value) {
|
|
var text = stringValue(value).trim();
|
|
|
|
return text || undefined;
|
|
}
|
|
|
|
function queryParam(search, name) {
|
|
return new URLSearchParams(stringValue(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 normalizeNullableId(value) {
|
|
if (value === undefined || value === null || value === '') return '';
|
|
return normalizeId(value);
|
|
}
|
|
|
|
function getCurrentGenealogyId(search) {
|
|
var source = search === undefined && root.location ? root.location.search : search;
|
|
var contextId;
|
|
|
|
if (search === undefined && root.ProfileUI && root.ProfileUI.getGenealogyId) {
|
|
contextId = root.ProfileUI.getGenealogyId();
|
|
if (contextId) return normalizeId(contextId);
|
|
}
|
|
return normalizeId(queryParam(source, 'genealogyId'));
|
|
}
|
|
|
|
function getCurrentArticleId(search) {
|
|
var source = search === undefined && root.location ? root.location.search : search;
|
|
|
|
return normalizeId(queryParam(source, 'articleId'));
|
|
}
|
|
|
|
function optionalSafeInteger(value) {
|
|
var number;
|
|
|
|
if (value === undefined || value === null || value === '') return undefined;
|
|
number = Number(value);
|
|
return Number.isSafeInteger(number) ? number : undefined;
|
|
}
|
|
|
|
function buildArticleBody(values) {
|
|
var source = values || {};
|
|
var body = {
|
|
articleTitle: stringValue(source.articleTitle).trim(),
|
|
articleContent: stringValue(source.articleContent).trim(),
|
|
status: '0'
|
|
};
|
|
var articleSummary = trimOrUndefined(source.articleSummary);
|
|
var coverOssId = normalizeNullableId(source.coverOssId);
|
|
var authorName = trimOrUndefined(source.authorName);
|
|
var sortOrder = optionalSafeInteger(source.sortOrder);
|
|
|
|
if (articleSummary !== undefined) body.articleSummary = articleSummary;
|
|
if (source.coverOssId !== undefined && source.coverOssId !== null && source.coverOssId !== '') {
|
|
if (coverOssId) body.coverOssId = coverOssId;
|
|
else body.invalidCoverOssId = true;
|
|
}
|
|
if (authorName !== undefined) body.authorName = authorName;
|
|
if (source.sortOrder !== undefined && source.sortOrder !== null && source.sortOrder !== '') {
|
|
if (sortOrder !== undefined) body.sortOrder = sortOrder;
|
|
else body.invalidSortOrder = true;
|
|
}
|
|
return body;
|
|
}
|
|
|
|
function validateArticleBody(body) {
|
|
if (!body || !body.articleTitle) return '请填写谱文标题';
|
|
if (!body.articleContent) return '请填写谱文正文';
|
|
if (body.invalidCoverOssId) return '封面文件编号无效,请重新选择文件';
|
|
if (body.invalidSortOrder) return '排序值必须是安全整数';
|
|
if (body.status === '1') return '当前 PC 无法重新读取停用谱文,暂不开放停用';
|
|
if (body.status !== '0') return '谱文状态只能是 0';
|
|
return '';
|
|
}
|
|
|
|
function normalizeArticle(item) {
|
|
var articleId = normalizeId(item && item.articleId);
|
|
var genealogyId = normalizeId(item && item.genealogyId);
|
|
var categoryId = normalizeNullableId(item && item.categoryId);
|
|
var coverFile = MediaDisplay && MediaDisplay.normalizeFileAccess(item && item.coverFile);
|
|
var articleTitle = stringValue(item && item.articleTitle).trim();
|
|
var articleContent = stringValue(item && item.articleContent).trim();
|
|
var viewCount = optionalSafeInteger(item && item.viewCount);
|
|
var sortOrder = optionalSafeInteger(item && item.sortOrder);
|
|
var status = stringValue(item && item.status);
|
|
var article;
|
|
|
|
if (!articleId || !genealogyId || !articleTitle || !articleContent) return null;
|
|
if (item.categoryId !== undefined && item.categoryId !== null && item.categoryId !== '' && !categoryId) return null;
|
|
if (item.coverFile !== undefined && item.coverFile !== null && !coverFile) return null;
|
|
if (item.viewCount !== undefined && item.viewCount !== null && item.viewCount !== '' && viewCount === undefined) return null;
|
|
if (item.sortOrder !== undefined && item.sortOrder !== null && item.sortOrder !== '' && sortOrder === undefined) return null;
|
|
if (status !== '0' && status !== '1') return null;
|
|
|
|
article = {
|
|
articleId: articleId,
|
|
genealogyId: genealogyId,
|
|
genealogyNo: stringValue(item.genealogyNo),
|
|
genealogyName: stringValue(item.genealogyName),
|
|
surname: stringValue(item.surname),
|
|
categoryName: stringValue(item.categoryName),
|
|
categoryCode: stringValue(item.categoryCode),
|
|
articleTitle: articleTitle,
|
|
articleSummary: stringValue(item.articleSummary),
|
|
articleContent: articleContent,
|
|
authorName: stringValue(item.authorName),
|
|
publishTime: stringValue(item.publishTime),
|
|
status: status,
|
|
remark: stringValue(item.remark)
|
|
};
|
|
if (categoryId) article.categoryId = categoryId;
|
|
if (coverFile) article.coverFile = coverFile;
|
|
if (viewCount !== undefined) article.viewCount = viewCount;
|
|
if (sortOrder !== undefined) article.sortOrder = sortOrder;
|
|
return article;
|
|
}
|
|
|
|
function normalizeArticles(data) {
|
|
var normalized;
|
|
|
|
if (!Array.isArray(data)) return [];
|
|
normalized = data.map(normalizeArticle);
|
|
return normalized.some(function (article) { return !article; }) ? [] : normalized;
|
|
}
|
|
|
|
function matchesSavedArticle(item, expectedArticleId) {
|
|
var article = normalizeArticle(item);
|
|
|
|
return Boolean(article && article.articleId === normalizeId(expectedArticleId));
|
|
}
|
|
|
|
function escapeHtml(value) {
|
|
return stringValue(value)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function buildListUrl(genealogyId, articleId) {
|
|
var url = 'profile-article.html?genealogyId=' + encodeURIComponent(genealogyId);
|
|
|
|
return articleId ? url + '&articleId=' + encodeURIComponent(articleId) : url;
|
|
}
|
|
|
|
function buildEditUrl(genealogyId, articleId) {
|
|
var url = 'profile-article-edit.html?genealogyId=' + encodeURIComponent(genealogyId);
|
|
|
|
return articleId ? url + '&articleId=' + encodeURIComponent(articleId) : url;
|
|
}
|
|
|
|
function canEditArticles(genealogy) {
|
|
return Boolean(genealogy && (genealogy.canEditContent === true || genealogy.canManage === true));
|
|
}
|
|
|
|
function detailValue(label, value) {
|
|
return '<p><span>' + escapeHtml(label) + '</span><b>' + escapeHtml(value || '未提供') + '</b></p>';
|
|
}
|
|
|
|
function renderArticleDetail(article) {
|
|
var actions = '';
|
|
|
|
if (!article) return stateHtml('empty', '请选择一篇谱文查看详情');
|
|
if (canEditContent) {
|
|
actions = '<div class="bottom-actions"><a class="btn ghost" href="' +
|
|
escapeHtml(buildEditUrl(article.genealogyId, article.articleId)) + '">编辑谱文</a>' +
|
|
'<button class="btn danger" type="button" data-article-delete-id="' +
|
|
escapeHtml(article.articleId) + '">删除谱文</button></div>';
|
|
}
|
|
return (MediaDisplay ? MediaDisplay.renderImage(article.coverFile, {
|
|
alt: article.articleTitle + '封面', className: 'article-cover-media'
|
|
}) : '') + '<div class="form-like article-detail">' +
|
|
detailValue('标题', article.articleTitle) +
|
|
detailValue('摘要', article.articleSummary) +
|
|
detailValue('正文', article.articleContent) +
|
|
detailValue('作者', article.authorName) +
|
|
detailValue('分类', article.categoryName) +
|
|
detailValue('发布时间', article.publishTime) +
|
|
detailValue('浏览量', article.viewCount === undefined ? '' : article.viewCount) +
|
|
detailValue('备注', article.remark) +
|
|
'</div>' + actions;
|
|
}
|
|
|
|
function renderArticleRow(article) {
|
|
var actions = '<button class="pill" type="button" data-article-detail-id="' +
|
|
escapeHtml(article.articleId) + '">查看详情</button>';
|
|
|
|
if (canEditContent) {
|
|
actions += '<a class="pill" href="' + escapeHtml(buildEditUrl(article.genealogyId, article.articleId)) +
|
|
'">编辑</a><button class="pill is-danger" type="button" data-article-delete-id="' +
|
|
escapeHtml(article.articleId) + '">删除</button>';
|
|
}
|
|
return '<article class="module-row article-row"><div><h3>' + escapeHtml(article.articleTitle) +
|
|
'</h3><p>' + escapeHtml(article.articleSummary || article.authorName || article.publishTime || '暂无摘要') +
|
|
'</p></div><div class="row-actions">' + actions + '</div></article>';
|
|
}
|
|
|
|
function shouldRedirectToLogin(api, error) {
|
|
var status = error && (error.status || error.code);
|
|
|
|
return !api || !api.getToken || !api.getToken() || Number(status) === 401;
|
|
}
|
|
|
|
function isForbidden(error) {
|
|
return Number(error && (error.status || error.code)) === 403;
|
|
}
|
|
|
|
function redirectUnauthorized(api, error) {
|
|
if (!shouldRedirectToLogin(api, error)) return false;
|
|
if (api && api.clearToken) api.clearToken();
|
|
if (!root.location) return true;
|
|
if (typeof root.location.replace === 'function') root.location.replace('login.html');
|
|
else root.location.href = 'login.html';
|
|
return true;
|
|
}
|
|
|
|
function showMessage(message) {
|
|
if (root.layui && root.layui.layer) root.layui.layer.msg(message);
|
|
else if (root.alert) root.alert(message);
|
|
}
|
|
|
|
function stateHtml(type, message) {
|
|
if (root.ProfileUI && root.ProfileUI.renderApiState) return root.ProfileUI.renderApiState(type, message);
|
|
return '<div class="api-state api-state--' + type + '" role="' +
|
|
(type === 'error' || type === 'forbidden' ? 'alert' : 'status') +
|
|
'" aria-live="polite">' + escapeHtml(message) + '</div>';
|
|
}
|
|
|
|
function setState(container, type, message) {
|
|
if (!container) return;
|
|
if (root.ProfileUI && root.ProfileUI.setApiState) {
|
|
root.ProfileUI.setApiState(container, type, message);
|
|
return;
|
|
}
|
|
container.innerHTML = stateHtml(type, message);
|
|
}
|
|
|
|
function getStateType(message) {
|
|
if (message.indexOf('无权') >= 0) return 'forbidden';
|
|
if (/失败|缺少/.test(message)) return 'error';
|
|
if (/正在|读取中|加载中/.test(message)) return 'loading';
|
|
return 'empty';
|
|
}
|
|
|
|
function syncLinks() {
|
|
if (root.ProfileUI && root.ProfileUI.syncGenealogyContextLinks) root.ProfileUI.syncGenealogyContextLinks();
|
|
}
|
|
|
|
function setFormStatus(message, type) {
|
|
var target = query('[data-article-form-status]');
|
|
|
|
if (!target) return;
|
|
if (!message) {
|
|
target.innerHTML = '';
|
|
return;
|
|
}
|
|
if (type && root.ProfileUI && root.ProfileUI.setApiState) {
|
|
root.ProfileUI.setApiState(target, type, message);
|
|
return;
|
|
}
|
|
target.textContent = message;
|
|
}
|
|
|
|
function setEditorEnabled(enabled, message, type) {
|
|
var placeholder = query('[data-article-editor-placeholder]');
|
|
|
|
queryAll('[data-article-editor], [data-article-editor-action]').forEach(function (element) {
|
|
element.hidden = !enabled;
|
|
});
|
|
if (placeholder) {
|
|
placeholder.hidden = Boolean(enabled);
|
|
if (message) setState(placeholder, type || getStateType(message), message);
|
|
}
|
|
}
|
|
|
|
function setWritePending(value, label) {
|
|
var submit = query('[data-article-form] [type="submit"]');
|
|
|
|
writePending = Boolean(value);
|
|
queryAll('[data-article-form] button, [data-article-form] input, [data-article-form] textarea, [data-article-delete-id]').forEach(function (control) {
|
|
control.disabled = writePending;
|
|
});
|
|
if (submit) {
|
|
if (!submit.dataset.defaultLabel) submit.dataset.defaultLabel = submit.textContent;
|
|
submit.textContent = writePending && label ? label : submit.dataset.defaultLabel;
|
|
submit.setAttribute('aria-busy', writePending ? 'true' : 'false');
|
|
}
|
|
}
|
|
|
|
function renderNoContext() {
|
|
var message = '请先选择家谱,再查看或维护谱文。';
|
|
var list = query('[data-article-list]');
|
|
var detail = query('[data-article-detail]');
|
|
|
|
setState(list, 'empty', message);
|
|
setState(detail, 'empty', '当前没有家谱上下文。');
|
|
setEditorEnabled(false, message, 'empty');
|
|
setFormStatus(message, 'empty');
|
|
}
|
|
|
|
function requireGenealogyId() {
|
|
var genealogyId = getCurrentGenealogyId();
|
|
|
|
if (!genealogyId) renderNoContext();
|
|
return genealogyId;
|
|
}
|
|
|
|
async function loadCapability(api, genealogyId) {
|
|
var detail = await api.genealogyDetail(genealogyId);
|
|
|
|
canEditContent = canEditArticles(detail);
|
|
queryAll('[data-article-create-link], [data-article-editor-action]').forEach(function (element) {
|
|
element.hidden = !canEditContent;
|
|
});
|
|
return detail;
|
|
}
|
|
|
|
function renderArticles(data) {
|
|
var container = query('[data-article-list]');
|
|
var articles = normalizeArticles(data);
|
|
|
|
articlesById = Object.create(null);
|
|
articles.forEach(function (article) {
|
|
articlesById[article.articleId] = article;
|
|
});
|
|
if (!container) return articles;
|
|
if (!Array.isArray(data) || (data.length && !articles.length)) {
|
|
setState(container, 'error', '谱文响应缺少稳定 ArticleVo 字段,请联系后端核对。');
|
|
return [];
|
|
}
|
|
if (!articles.length) {
|
|
setState(container, 'empty', '暂无谱文');
|
|
return [];
|
|
}
|
|
container.innerHTML = articles.map(renderArticleRow).join('');
|
|
return articles;
|
|
}
|
|
|
|
function renderDetail(article) {
|
|
var container = query('[data-article-detail]');
|
|
|
|
if (container) container.innerHTML = renderArticleDetail(article);
|
|
}
|
|
|
|
async function loadArticleDetail(articleId) {
|
|
var api = getApi();
|
|
var genealogyId = requireGenealogyId();
|
|
var article;
|
|
|
|
if (!genealogyId || !normalizeId(articleId) || redirectUnauthorized(api)) return null;
|
|
setState(query('[data-article-detail]'), 'loading', '正在加载谱文详情…');
|
|
try {
|
|
article = normalizeArticle(await api.articleDetail(genealogyId, articleId));
|
|
if (!article || article.articleId !== normalizeId(articleId)) {
|
|
throw new Error('谱文详情响应缺少稳定 ArticleVo 字段');
|
|
}
|
|
articlesById[article.articleId] = article;
|
|
renderDetail(article);
|
|
return article;
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return null;
|
|
setState(query('[data-article-detail]'), isForbidden(error) ? 'forbidden' : 'error',
|
|
isForbidden(error) ? '当前账号无权查看该谱文。' : '谱文详情加载失败,请稍后重试。');
|
|
showMessage(isForbidden(error) ? '当前账号无权查看该谱文。' : (error.message || '谱文详情加载失败'));
|
|
return null;
|
|
}
|
|
}
|
|
|
|
async function loadArticles() {
|
|
var api = getApi();
|
|
var genealogyId;
|
|
var articles;
|
|
var requestedId;
|
|
|
|
if (redirectUnauthorized(api)) return;
|
|
genealogyId = requireGenealogyId();
|
|
if (!genealogyId) return;
|
|
syncLinks();
|
|
setState(query('[data-article-list]'), 'loading', '正在加载谱文…');
|
|
try {
|
|
await loadCapability(api, genealogyId);
|
|
articles = renderArticles(await api.articles(genealogyId));
|
|
requestedId = getCurrentArticleId();
|
|
if (requestedId && articlesById[requestedId]) await loadArticleDetail(requestedId);
|
|
else if (articles.length) await loadArticleDetail(articles[0].articleId);
|
|
else renderDetail(null);
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
setState(query('[data-article-list]'), isForbidden(error) ? 'forbidden' : 'error',
|
|
isForbidden(error) ? '当前账号无权查看该家谱的谱文。' : '谱文加载失败,请稍后重试。');
|
|
showMessage(isForbidden(error) ? '当前账号无权查看该家谱的谱文。' : (error.message || '谱文加载失败'));
|
|
}
|
|
}
|
|
|
|
function getFormValues(form) {
|
|
var values = {};
|
|
|
|
queryAll('[name]', form).forEach(function (field) {
|
|
values[field.name] = field.value;
|
|
});
|
|
return values;
|
|
}
|
|
|
|
function setFieldValue(name, value) {
|
|
var field = query('[data-article-form] [name="' + name + '"]');
|
|
|
|
if (field) field.value = value === undefined || value === null ? '' : String(value);
|
|
}
|
|
|
|
function fillArticleForm(article) {
|
|
var contentField;
|
|
var contentEditor;
|
|
|
|
if (!article || article.status !== '0') throw new Error('停用谱文无法通过当前 PC 接口重新读取或编辑');
|
|
setFieldValue('articleTitle', article.articleTitle);
|
|
setFieldValue('articleSummary', article.articleSummary);
|
|
setFieldValue('coverOssId', article.coverOssId);
|
|
setFieldValue('articleContent', article.articleContent);
|
|
setFieldValue('authorName', article.authorName);
|
|
setFieldValue('sortOrder', article.sortOrder);
|
|
setFieldValue('status', '0');
|
|
contentField = query('[name="articleContent"]');
|
|
if (root.AppRichEditor && root.AppRichEditor.init && contentField) {
|
|
contentEditor = root.AppRichEditor.init(contentField);
|
|
if (contentEditor && contentEditor.editor && contentEditor.editor.setHtml) {
|
|
contentEditor.editor.setHtml(article.articleContent);
|
|
contentEditor.sync();
|
|
}
|
|
}
|
|
}
|
|
|
|
async function loadArticleEditor() {
|
|
var api = getApi();
|
|
var genealogyId;
|
|
var articleId;
|
|
var article;
|
|
|
|
if (redirectUnauthorized(api)) return;
|
|
genealogyId = requireGenealogyId();
|
|
if (!genealogyId) return;
|
|
articleId = getCurrentArticleId();
|
|
syncLinks();
|
|
setEditorEnabled(false, '正在加载谱文编辑信息…', 'loading');
|
|
setFormStatus('正在读取家谱权限…', 'loading');
|
|
try {
|
|
await loadCapability(api, genealogyId);
|
|
if (!canEditContent) throw { status: 403, message: '当前账号无权维护该家谱的谱文。' };
|
|
if (articleId) {
|
|
article = normalizeArticle(await api.articleDetail(genealogyId, articleId));
|
|
if (!article || article.articleId !== articleId) throw new Error('谱文详情响应缺少稳定 ArticleVo 字段');
|
|
fillArticleForm(article);
|
|
if (query('[data-article-editor-title]')) query('[data-article-editor-title]').textContent = '编辑谱文';
|
|
}
|
|
setEditorEnabled(true);
|
|
setFormStatus('');
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
setEditorEnabled(false, isForbidden(error)
|
|
? '当前账号无权维护该家谱的谱文。'
|
|
: (error.message || '谱文编辑信息加载失败'), isForbidden(error) ? 'forbidden' : 'error');
|
|
setFormStatus(
|
|
isForbidden(error) ? '当前账号无权维护该家谱的谱文。' : (error.message || '谱文编辑信息加载失败'),
|
|
isForbidden(error) ? 'forbidden' : 'error'
|
|
);
|
|
}
|
|
}
|
|
|
|
async function submitArticle(form) {
|
|
var api = getApi();
|
|
var genealogyId = requireGenealogyId();
|
|
var articleId = getCurrentArticleId();
|
|
var body;
|
|
var validation;
|
|
var result;
|
|
var saved;
|
|
var savedId;
|
|
|
|
if (writePending || !canEditContent || !genealogyId || redirectUnauthorized(api)) return;
|
|
if (root.AppRichEditor && root.AppRichEditor.syncAll) root.AppRichEditor.syncAll();
|
|
body = buildArticleBody(getFormValues(form));
|
|
validation = validateArticleBody(body);
|
|
if (validation) {
|
|
setFormStatus(validation, 'error');
|
|
return;
|
|
}
|
|
delete body.invalidCoverOssId;
|
|
delete body.invalidSortOrder;
|
|
setWritePending(true, '正在保存…');
|
|
setFormStatus('正在保存谱文…', 'loading');
|
|
try {
|
|
result = articleId
|
|
? await api.updateArticle(genealogyId, articleId, body)
|
|
: await api.createArticle(genealogyId, body);
|
|
saved = normalizeArticle(result);
|
|
savedId = articleId || (saved && saved.articleId);
|
|
if (!savedId) throw new Error('保存响应缺少 articleId');
|
|
if (!matchesSavedArticle(await api.articleDetail(genealogyId, savedId), savedId)) {
|
|
throw new Error('保存后重读未返回同一篇谱文');
|
|
}
|
|
if (root.location) root.location.href = buildListUrl(genealogyId, savedId);
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
setFormStatus(
|
|
isForbidden(error) ? '当前账号无权保存该谱文。' : (error.message || '谱文保存失败'),
|
|
isForbidden(error) ? 'forbidden' : 'error'
|
|
);
|
|
} finally {
|
|
setWritePending(false);
|
|
}
|
|
}
|
|
|
|
async function deleteArticle(articleId) {
|
|
var api = getApi();
|
|
var genealogyId = requireGenealogyId();
|
|
var article = articlesById[articleId];
|
|
|
|
if (writePending || !canEditContent || !genealogyId || !normalizeId(articleId) || redirectUnauthorized(api)) return;
|
|
if (!await confirmAction('确认删除“' + (article ? article.articleTitle : '该谱文') + '”吗?删除后不可恢复。')) return;
|
|
setWritePending(true);
|
|
try {
|
|
await api.deleteArticle(genealogyId, articleId);
|
|
await loadArticles();
|
|
showMessage('谱文已删除');
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
showMessage(isForbidden(error) ? '当前账号无权删除该谱文。' : (error.message || '谱文删除失败'));
|
|
} finally {
|
|
setWritePending(false);
|
|
}
|
|
}
|
|
|
|
function bindActions() {
|
|
if (!documentRef) return;
|
|
documentRef.addEventListener('click', function (event) {
|
|
var detailButton = event.target.closest('[data-article-detail-id]');
|
|
var deleteButton = event.target.closest('[data-article-delete-id]');
|
|
|
|
if (detailButton) {
|
|
event.preventDefault();
|
|
loadArticleDetail(detailButton.getAttribute('data-article-detail-id'));
|
|
return;
|
|
}
|
|
if (deleteButton) {
|
|
event.preventDefault();
|
|
deleteArticle(deleteButton.getAttribute('data-article-delete-id'));
|
|
}
|
|
});
|
|
documentRef.addEventListener('submit', function (event) {
|
|
var form = event.target.closest('[data-article-form]');
|
|
|
|
if (!form) return;
|
|
event.preventDefault();
|
|
submitArticle(form);
|
|
});
|
|
}
|
|
|
|
function init() {
|
|
if (!documentRef) return;
|
|
bindActions();
|
|
if (query('[data-article-page]')) loadArticles();
|
|
if (query('[data-article-edit-page]')) loadArticleEditor();
|
|
}
|
|
|
|
return {
|
|
getCurrentGenealogyId: getCurrentGenealogyId,
|
|
getCurrentArticleId: getCurrentArticleId,
|
|
buildArticleBody: buildArticleBody,
|
|
validateArticleBody: validateArticleBody,
|
|
normalizeArticle: normalizeArticle,
|
|
canEditArticles: canEditArticles,
|
|
normalizeArticles: normalizeArticles,
|
|
matchesSavedArticle: matchesSavedArticle,
|
|
renderArticleDetail: renderArticleDetail,
|
|
shouldRedirectToLogin: shouldRedirectToLogin,
|
|
isForbidden: isForbidden,
|
|
init: init
|
|
};
|
|
});
|