816 lines
30 KiB
JavaScript
816 lines
30 KiB
JavaScript
(function (root, factory) {
|
|
if (typeof module === 'object' && module.exports) {
|
|
module.exports = factory(root);
|
|
return;
|
|
}
|
|
|
|
root.AlbumPages = factory(root);
|
|
if (root.document) {
|
|
root.document.addEventListener('DOMContentLoaded', function () {
|
|
root.AlbumPages.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 albumsById = Object.create(null);
|
|
var photosById = Object.create(null);
|
|
var currentAlbum = null;
|
|
var canEditContent = false;
|
|
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 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 optionalSafeInteger(value) {
|
|
var number;
|
|
|
|
if (value === undefined || value === null || value === '') return undefined;
|
|
number = Number(value);
|
|
return Number.isSafeInteger(number) ? number : undefined;
|
|
}
|
|
|
|
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 getCurrentAlbumId(search) {
|
|
var source = search === undefined && root.location ? root.location.search : search;
|
|
|
|
return normalizeId(queryParam(source, 'albumId'));
|
|
}
|
|
|
|
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 = stringValue(value);
|
|
|
|
if (!/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/.test(text)) return '';
|
|
return text.slice(0, 16).replace(' ', 'T');
|
|
}
|
|
|
|
function isValidBackendDateTime(value) {
|
|
var match = stringValue(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 buildAlbumBody(values) {
|
|
var source = values || {};
|
|
var body = {
|
|
albumName: stringValue(source.albumName).trim(),
|
|
status: '0'
|
|
};
|
|
var albumDesc = trimOrUndefined(source.albumDesc);
|
|
var coverOssId = normalizeNullableId(source.coverOssId);
|
|
var sortOrder = optionalSafeInteger(source.sortOrder);
|
|
|
|
if (albumDesc !== undefined) body.albumDesc = albumDesc;
|
|
if (source.coverOssId !== undefined && source.coverOssId !== null && source.coverOssId !== '') {
|
|
if (coverOssId) body.coverOssId = coverOssId;
|
|
else body.invalidCoverOssId = true;
|
|
}
|
|
if (source.sortOrder !== undefined && source.sortOrder !== null && source.sortOrder !== '') {
|
|
if (sortOrder !== undefined) body.sortOrder = sortOrder;
|
|
else body.invalidSortOrder = true;
|
|
}
|
|
return body;
|
|
}
|
|
|
|
function validateAlbumBody(body) {
|
|
if (!body || !body.albumName) return '请填写相册名称';
|
|
if (body.invalidCoverOssId) return '封面文件编号无效,请重新选择文件';
|
|
if (body.invalidSortOrder) return '排序值必须是安全整数';
|
|
if (body.status === '1') return '当前 PC 无法重新读取停用相册,暂不开放停用';
|
|
if (body.status !== '0') return '相册状态只能是 0';
|
|
return '';
|
|
}
|
|
|
|
function buildAlbumPhotoBody(values) {
|
|
var source = values || {};
|
|
var body = { status: '0' };
|
|
var ossId = normalizeNullableId(source.ossId);
|
|
var photoTitle = trimOrUndefined(source.photoTitle);
|
|
var photoDesc = trimOrUndefined(source.photoDesc);
|
|
var photographer = trimOrUndefined(source.photographer);
|
|
var shootTime = toBackendDateTime(source.shootTime);
|
|
var sortOrder = optionalSafeInteger(source.sortOrder);
|
|
|
|
if (ossId) body.ossId = ossId;
|
|
else if (source.ossId !== undefined && source.ossId !== null && source.ossId !== '') body.invalidOssId = true;
|
|
if (photoTitle !== undefined) body.photoTitle = photoTitle;
|
|
if (photoDesc !== undefined) body.photoDesc = photoDesc;
|
|
if (photographer !== undefined) body.photographer = photographer;
|
|
if (shootTime !== undefined) body.shootTime = shootTime;
|
|
if (source.sortOrder !== undefined && source.sortOrder !== null && source.sortOrder !== '') {
|
|
if (sortOrder !== undefined) body.sortOrder = sortOrder;
|
|
else body.invalidSortOrder = true;
|
|
}
|
|
return body;
|
|
}
|
|
|
|
function validateAlbumPhotoBody(body) {
|
|
if (!body || !body.ossId || body.invalidOssId) return '请选择并上传照片';
|
|
if (body.shootTime !== undefined && !isValidBackendDateTime(body.shootTime)) return '拍摄时间格式无效';
|
|
if (body.invalidSortOrder) return '排序值必须是安全整数';
|
|
if (body.status === '1') return '当前 PC 无法重新读取停用照片,暂不开放停用';
|
|
if (body.status !== '0') return '照片状态只能是 0';
|
|
return '';
|
|
}
|
|
|
|
function normalizeAlbum(item) {
|
|
var albumId = normalizeId(item && item.albumId);
|
|
var genealogyId = normalizeId(item && item.genealogyId);
|
|
var coverFile = MediaDisplay && MediaDisplay.normalizeFileAccess(item && item.coverFile);
|
|
var albumName = stringValue(item && item.albumName).trim();
|
|
var photoCount = optionalSafeInteger(item && item.photoCount);
|
|
var sortOrder = optionalSafeInteger(item && item.sortOrder);
|
|
var status = stringValue(item && item.status);
|
|
var album;
|
|
|
|
if (!albumId || !genealogyId || !albumName) return null;
|
|
if (item.coverFile !== undefined && item.coverFile !== null && !coverFile) return null;
|
|
if (item.photoCount !== undefined && item.photoCount !== null && item.photoCount !== '' && photoCount === undefined) return null;
|
|
if (item.sortOrder !== undefined && item.sortOrder !== null && item.sortOrder !== '' && sortOrder === undefined) return null;
|
|
if (status !== '0' && status !== '1') return null;
|
|
album = {
|
|
albumId: albumId,
|
|
genealogyId: genealogyId,
|
|
genealogyNo: stringValue(item.genealogyNo),
|
|
genealogyName: stringValue(item.genealogyName),
|
|
surname: stringValue(item.surname),
|
|
albumName: albumName,
|
|
albumDesc: stringValue(item.albumDesc),
|
|
status: status,
|
|
remark: stringValue(item.remark)
|
|
};
|
|
if (coverFile) album.coverFile = coverFile;
|
|
if (photoCount !== undefined) album.photoCount = photoCount;
|
|
if (sortOrder !== undefined) album.sortOrder = sortOrder;
|
|
return album;
|
|
}
|
|
|
|
function normalizeAlbumPhoto(item) {
|
|
var photoId = normalizeId(item && item.photoId);
|
|
var genealogyId = normalizeId(item && item.genealogyId);
|
|
var albumId = normalizeId(item && item.albumId);
|
|
var photoFile = MediaDisplay && MediaDisplay.normalizeFileAccess(item && item.photoFile);
|
|
var sortOrder = optionalSafeInteger(item && item.sortOrder);
|
|
var status = stringValue(item && item.status);
|
|
var photo;
|
|
|
|
if (!photoId || !genealogyId || !albumId || !photoFile) return null;
|
|
if (item.sortOrder !== undefined && item.sortOrder !== null && item.sortOrder !== '' && sortOrder === undefined) return null;
|
|
if (status !== '0' && status !== '1') return null;
|
|
photo = {
|
|
photoId: photoId,
|
|
genealogyId: genealogyId,
|
|
genealogyNo: stringValue(item.genealogyNo),
|
|
genealogyName: stringValue(item.genealogyName),
|
|
surname: stringValue(item.surname),
|
|
albumId: albumId,
|
|
albumName: stringValue(item.albumName),
|
|
photoFile: photoFile,
|
|
photoTitle: stringValue(item.photoTitle),
|
|
photoDesc: stringValue(item.photoDesc),
|
|
photographer: stringValue(item.photographer),
|
|
shootTime: stringValue(item.shootTime),
|
|
status: status,
|
|
remark: stringValue(item.remark)
|
|
};
|
|
if (sortOrder !== undefined) photo.sortOrder = sortOrder;
|
|
return photo;
|
|
}
|
|
|
|
function normalizeAlbums(data) {
|
|
var normalized;
|
|
|
|
if (!Array.isArray(data)) return [];
|
|
normalized = data.map(normalizeAlbum);
|
|
return normalized.some(function (album) { return !album; }) ? [] : normalized;
|
|
}
|
|
|
|
function normalizeAlbumPhotos(data) {
|
|
var normalized;
|
|
|
|
if (!Array.isArray(data)) return [];
|
|
normalized = data.map(normalizeAlbumPhoto);
|
|
return normalized.some(function (photo) { return !photo; }) ? [] : normalized;
|
|
}
|
|
|
|
function findAlbumById(data, albumId) {
|
|
var albums = normalizeAlbums(data);
|
|
var expectedId = normalizeId(albumId);
|
|
|
|
return albums.find(function (album) { return album.albumId === expectedId; }) || null;
|
|
}
|
|
|
|
function escapeHtml(value) {
|
|
return stringValue(value)
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/"/g, '"')
|
|
.replace(/'/g, ''');
|
|
}
|
|
|
|
function buildListUrl(genealogyId) {
|
|
return 'profile-album.html?genealogyId=' + encodeURIComponent(genealogyId);
|
|
}
|
|
|
|
function buildEditUrl(genealogyId, albumId) {
|
|
var url = 'profile-album-edit.html?genealogyId=' + encodeURIComponent(genealogyId);
|
|
|
|
return albumId ? url + '&albumId=' + encodeURIComponent(albumId) : url;
|
|
}
|
|
|
|
function buildDetailUrl(genealogyId, albumId) {
|
|
return 'profile-album-detail.html?genealogyId=' + encodeURIComponent(genealogyId) +
|
|
'&albumId=' + encodeURIComponent(albumId);
|
|
}
|
|
|
|
function detailValue(label, value) {
|
|
return '<p><span>' + escapeHtml(label) + '</span><b>' + escapeHtml(value || '未提供') + '</b></p>';
|
|
}
|
|
|
|
function renderAlbumDetail(album) {
|
|
var actions = '';
|
|
|
|
if (!album) return '';
|
|
if (canEditContent) {
|
|
actions = '<div class="bottom-actions"><a class="btn ghost" href="' +
|
|
escapeHtml(buildEditUrl(album.genealogyId, album.albumId)) + '">编辑相册</a>' +
|
|
'<button class="btn danger" type="button" data-album-delete-id="' +
|
|
escapeHtml(album.albumId) + '">删除相册</button></div>';
|
|
}
|
|
return (MediaDisplay ? MediaDisplay.renderImage(album.coverFile, {
|
|
alt: album.albumName + '封面', className: 'album-cover-media'
|
|
}) : '') + '<div class="form-like album-detail">' +
|
|
detailValue('相册名称', album.albumName) +
|
|
detailValue('相册说明', album.albumDesc) +
|
|
detailValue('照片数量', album.photoCount === undefined ? '' : album.photoCount) +
|
|
detailValue('备注', album.remark) +
|
|
'</div>' + actions;
|
|
}
|
|
|
|
function renderAlbumRow(album) {
|
|
var actions = '<a class="pill" href="' + escapeHtml(buildDetailUrl(album.genealogyId, album.albumId)) +
|
|
'">查看照片</a>';
|
|
|
|
if (canEditContent) {
|
|
actions += '<a class="pill" href="' + escapeHtml(buildEditUrl(album.genealogyId, album.albumId)) +
|
|
'">编辑</a><button class="pill is-danger" type="button" data-album-delete-id="' +
|
|
escapeHtml(album.albumId) + '">删除</button>';
|
|
}
|
|
return '<article class="module-row album-row"><div><h3>' + escapeHtml(album.albumName) +
|
|
'</h3><p>' + escapeHtml(album.albumDesc || ('照片数量:' + (album.photoCount || 0))) +
|
|
'</p></div><div class="row-actions">' + actions + '</div></article>';
|
|
}
|
|
|
|
function renderPhotoList(data) {
|
|
var photos = Array.isArray(data) ? data.map(normalizeAlbumPhoto) : [];
|
|
|
|
if (photos.some(function (photo) { return !photo; })) return '';
|
|
if (!photos.length) return '';
|
|
return photos.map(function (photo) {
|
|
var action = canEditContent
|
|
? '<button class="pill is-danger" type="button" data-album-photo-delete-id="' +
|
|
escapeHtml(photo.photoId) + '">删除</button>'
|
|
: '';
|
|
return '<article class="module-row album-photo-row">' +
|
|
(MediaDisplay ? MediaDisplay.renderImage(photo.photoFile, {
|
|
alt: photo.photoTitle || '家族照片', className: 'album-photo-media'
|
|
}) : '') + '<div><h3>' +
|
|
escapeHtml(photo.photoTitle || '未命名照片') + '</h3><p>' +
|
|
escapeHtml([photo.photoDesc, photo.photographer, photo.shootTime].filter(Boolean).join(' · ') || '文件已上传') +
|
|
'</p></div><div class="row-actions">' + action + '</div></article>';
|
|
}).join('');
|
|
}
|
|
|
|
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 syncLinks() {
|
|
if (root.ProfileUI && root.ProfileUI.syncGenealogyContextLinks) root.ProfileUI.syncGenealogyContextLinks();
|
|
}
|
|
|
|
function getApiStateType(error) {
|
|
return isForbidden(error) ? 'forbidden' : 'error';
|
|
}
|
|
|
|
function setStatus(selector, message, type) {
|
|
var target = query(selector);
|
|
|
|
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) {
|
|
var placeholder = query('[data-album-editor-placeholder]');
|
|
|
|
queryAll('[data-album-editor], [data-album-editor-action], [data-album-photo-editor]').forEach(function (element) {
|
|
element.hidden = !enabled;
|
|
});
|
|
if (placeholder) {
|
|
placeholder.hidden = Boolean(enabled);
|
|
if (message) placeholder.textContent = message;
|
|
}
|
|
}
|
|
|
|
function setWritePending(value) {
|
|
writePending = Boolean(value);
|
|
queryAll('[data-album-form] button, [data-album-form] input, [data-album-form] textarea, [data-album-photo-form] button, [data-album-photo-form] input, [data-album-photo-form] textarea, [data-album-delete-id], [data-album-photo-delete-id]').forEach(function (control) {
|
|
control.disabled = writePending;
|
|
});
|
|
}
|
|
|
|
function renderNoContext() {
|
|
var message = '请先选择家谱,再查看或维护相册。';
|
|
|
|
setStatus('[data-album-list]', message, 'empty');
|
|
setStatus('[data-album-detail]', '当前没有家谱上下文。', 'empty');
|
|
setStatus('[data-album-photo-list]', '当前没有家谱上下文。', 'empty');
|
|
setEditorEnabled(false, message);
|
|
setStatus('[data-album-form-status]', message, 'forbidden');
|
|
setStatus('[data-album-photo-status]', message, 'forbidden');
|
|
}
|
|
|
|
function requireGenealogyId() {
|
|
var genealogyId = getCurrentGenealogyId();
|
|
|
|
if (!genealogyId) renderNoContext();
|
|
return genealogyId;
|
|
}
|
|
|
|
function canEditAlbums(genealogy) {
|
|
return Boolean(genealogy && (genealogy.canEditContent === true || genealogy.canManage === true));
|
|
}
|
|
|
|
async function loadCapability(api, genealogyId) {
|
|
var detail = await api.genealogyDetail(genealogyId);
|
|
|
|
canEditContent = canEditAlbums(detail);
|
|
queryAll('[data-album-create-link], [data-album-editor-action], [data-album-photo-editor]').forEach(function (element) {
|
|
element.hidden = !canEditContent;
|
|
});
|
|
return detail;
|
|
}
|
|
|
|
function renderAlbums(data) {
|
|
var container = query('[data-album-list]');
|
|
var albums = normalizeAlbums(data);
|
|
|
|
albumsById = Object.create(null);
|
|
albums.forEach(function (album) { albumsById[album.albumId] = album; });
|
|
if (!container) return albums;
|
|
if (!Array.isArray(data) || (data.length && !albums.length)) {
|
|
setStatus('[data-album-list]', '相册响应缺少稳定 AlbumVo 字段,请联系后端核对。', 'error');
|
|
return [];
|
|
}
|
|
if (!albums.length) {
|
|
setStatus('[data-album-list]', '暂无相册', 'empty');
|
|
return [];
|
|
}
|
|
container.innerHTML = albums.map(renderAlbumRow).join('');
|
|
return albums;
|
|
}
|
|
|
|
async function loadAlbums() {
|
|
var api = getApi();
|
|
var genealogyId;
|
|
|
|
if (redirectUnauthorized(api)) return;
|
|
genealogyId = requireGenealogyId();
|
|
if (!genealogyId) return;
|
|
syncLinks();
|
|
setStatus('[data-album-list]', '正在加载相册…', 'loading');
|
|
try {
|
|
await loadCapability(api, genealogyId);
|
|
renderAlbums(await api.albums(genealogyId));
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
setStatus(
|
|
'[data-album-list]',
|
|
isForbidden(error) ? '当前账号无权查看该家谱的相册。' : '相册加载失败,请稍后重试。',
|
|
getApiStateType(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(formSelector, name, value) {
|
|
var field = query(formSelector + ' [name="' + name + '"]');
|
|
|
|
if (field) field.value = value === undefined || value === null ? '' : String(value);
|
|
}
|
|
|
|
function fillAlbumForm(album) {
|
|
if (!album || album.status !== '0') throw new Error('停用相册无法通过当前 PC 接口重新读取或编辑');
|
|
setFieldValue('[data-album-form]', 'albumName', album.albumName);
|
|
setFieldValue('[data-album-form]', 'albumDesc', album.albumDesc);
|
|
setFieldValue('[data-album-form]', 'coverOssId', album.coverOssId);
|
|
setFieldValue('[data-album-form]', 'sortOrder', album.sortOrder);
|
|
setFieldValue('[data-album-form]', 'status', '0');
|
|
}
|
|
|
|
async function loadAlbumEditor() {
|
|
var api = getApi();
|
|
var genealogyId;
|
|
var albumId;
|
|
var albums;
|
|
var album;
|
|
|
|
if (redirectUnauthorized(api)) return;
|
|
genealogyId = requireGenealogyId();
|
|
if (!genealogyId) return;
|
|
albumId = getCurrentAlbumId();
|
|
syncLinks();
|
|
setEditorEnabled(false, '正在加载相册编辑信息…');
|
|
setStatus('[data-album-form-status]', '正在读取家谱权限…', 'loading');
|
|
try {
|
|
await loadCapability(api, genealogyId);
|
|
if (!canEditContent) throw { status: 403, message: '当前账号无权维护该家谱的相册。' };
|
|
if (albumId) {
|
|
albums = await api.albums(genealogyId);
|
|
album = findAlbumById(albums, albumId);
|
|
if (!album) throw new Error('相册列表未返回当前相册');
|
|
fillAlbumForm(album);
|
|
if (query('[data-album-editor-title]')) query('[data-album-editor-title]').textContent = '编辑相册';
|
|
}
|
|
setEditorEnabled(true);
|
|
setStatus('[data-album-form-status]', '');
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
setEditorEnabled(false, isForbidden(error) ? '当前账号无权维护该家谱的相册。' : (error.message || '相册编辑信息加载失败'));
|
|
setStatus(
|
|
'[data-album-form-status]',
|
|
isForbidden(error) ? '当前账号无权维护该家谱的相册。' : (error.message || '相册编辑信息加载失败'),
|
|
getApiStateType(error)
|
|
);
|
|
}
|
|
}
|
|
|
|
async function loadPhotos(api, genealogyId, albumId) {
|
|
var data;
|
|
var photos;
|
|
var container = query('[data-album-photo-list]');
|
|
|
|
setStatus('[data-album-photo-list]', '正在加载照片…', 'loading');
|
|
data = await api.albumPhotos(genealogyId, albumId);
|
|
photos = normalizeAlbumPhotos(data);
|
|
photosById = Object.create(null);
|
|
photos.forEach(function (photo) { photosById[photo.photoId] = photo; });
|
|
if (!container) return photos;
|
|
if (!Array.isArray(data) || (data.length && !photos.length)) {
|
|
setStatus('[data-album-photo-list]', '照片响应缺少稳定 AlbumPhotoVo 字段,请联系后端核对。', 'error');
|
|
return [];
|
|
}
|
|
if (!photos.length) {
|
|
setStatus('[data-album-photo-list]', '暂无照片', 'empty');
|
|
return [];
|
|
}
|
|
container.innerHTML = renderPhotoList(photos);
|
|
return photos;
|
|
}
|
|
|
|
async function loadAlbumDetailPage() {
|
|
var api = getApi();
|
|
var genealogyId;
|
|
var albumId;
|
|
var albums;
|
|
|
|
if (redirectUnauthorized(api)) return;
|
|
genealogyId = requireGenealogyId();
|
|
albumId = getCurrentAlbumId();
|
|
if (!genealogyId || !albumId) {
|
|
if (genealogyId) {
|
|
setStatus('[data-album-detail]', '缺少有效相册编号。', 'error');
|
|
setStatus('[data-album-photo-list]', '缺少有效相册编号。', 'empty');
|
|
}
|
|
return;
|
|
}
|
|
syncLinks();
|
|
setStatus('[data-album-detail]', '正在加载相册信息…', 'loading');
|
|
setStatus('[data-album-photo-list]', '正在加载照片…', 'loading');
|
|
try {
|
|
await loadCapability(api, genealogyId);
|
|
albums = await api.albums(genealogyId);
|
|
currentAlbum = findAlbumById(albums, albumId);
|
|
if (!currentAlbum) throw new Error('相册列表未返回当前相册');
|
|
if (query('[data-album-detail]')) query('[data-album-detail]').innerHTML = renderAlbumDetail(currentAlbum);
|
|
if (query('[data-album-detail-title]')) query('[data-album-detail-title]').textContent = currentAlbum.albumName;
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
setStatus(
|
|
'[data-album-detail]',
|
|
isForbidden(error) ? '当前账号无权查看该相册。' : (error.message || '相册详情加载失败'),
|
|
getApiStateType(error)
|
|
);
|
|
setStatus(
|
|
'[data-album-photo-list]',
|
|
isForbidden(error) ? '当前账号无权查看该相册。' : '照片加载失败,请稍后重试。',
|
|
getApiStateType(error)
|
|
);
|
|
showMessage(isForbidden(error) ? '当前账号无权查看该相册。' : (error.message || '相册详情加载失败'));
|
|
return;
|
|
}
|
|
try {
|
|
await loadPhotos(api, genealogyId, albumId);
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
setStatus(
|
|
'[data-album-photo-list]',
|
|
isForbidden(error) ? '当前账号无权查看该相册照片。' : (error.message || '照片加载失败'),
|
|
getApiStateType(error)
|
|
);
|
|
showMessage(isForbidden(error) ? '当前账号无权查看该相册照片。' : (error.message || '照片加载失败'));
|
|
}
|
|
}
|
|
|
|
async function submitAlbum(form) {
|
|
var api = getApi();
|
|
var genealogyId = requireGenealogyId();
|
|
var albumId = getCurrentAlbumId();
|
|
var body;
|
|
var validation;
|
|
var result;
|
|
var saved;
|
|
var savedId;
|
|
var verified;
|
|
|
|
if (writePending || !canEditContent || !genealogyId || redirectUnauthorized(api)) return;
|
|
body = buildAlbumBody(getFormValues(form));
|
|
validation = validateAlbumBody(body);
|
|
if (validation) {
|
|
setStatus('[data-album-form-status]', validation, 'error');
|
|
return;
|
|
}
|
|
delete body.invalidCoverOssId;
|
|
delete body.invalidSortOrder;
|
|
setWritePending(true);
|
|
setStatus('[data-album-form-status]', '正在保存相册…', 'loading');
|
|
try {
|
|
result = albumId
|
|
? await api.updateAlbum(genealogyId, albumId, body)
|
|
: await api.createAlbum(genealogyId, body);
|
|
saved = normalizeAlbum(result);
|
|
savedId = albumId || (saved && saved.albumId);
|
|
if (!savedId) throw new Error('保存响应缺少 albumId');
|
|
verified = findAlbumById(await api.albums(genealogyId), savedId);
|
|
if (!verified) throw new Error('保存后相册列表未返回同一相册');
|
|
if (root.location) root.location.href = buildDetailUrl(genealogyId, savedId);
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
setStatus(
|
|
'[data-album-form-status]',
|
|
isForbidden(error) ? '当前账号无权保存该相册。' : (error.message || '相册保存失败'),
|
|
getApiStateType(error)
|
|
);
|
|
} finally {
|
|
setWritePending(false);
|
|
}
|
|
}
|
|
|
|
async function submitPhoto(form) {
|
|
var api = getApi();
|
|
var genealogyId = requireGenealogyId();
|
|
var albumId = getCurrentAlbumId();
|
|
var body;
|
|
var validation;
|
|
var saved;
|
|
|
|
if (writePending || !canEditContent || !genealogyId || !albumId || redirectUnauthorized(api)) return;
|
|
body = buildAlbumPhotoBody(getFormValues(form));
|
|
validation = validateAlbumPhotoBody(body);
|
|
if (validation) {
|
|
setStatus('[data-album-photo-status]', validation, 'error');
|
|
return;
|
|
}
|
|
delete body.invalidOssId;
|
|
delete body.invalidSortOrder;
|
|
setWritePending(true);
|
|
setStatus('[data-album-photo-status]', '正在添加照片…', 'loading');
|
|
try {
|
|
saved = normalizeAlbumPhoto(await api.createAlbumPhoto(genealogyId, albumId, body));
|
|
if (!saved || saved.albumId !== albumId) throw new Error('添加照片响应缺少稳定 photoId');
|
|
if (!(await loadPhotos(api, genealogyId, albumId)).some(function (photo) { return photo.photoId === saved.photoId; })) {
|
|
throw new Error('添加后照片列表未返回同一照片');
|
|
}
|
|
form.reset();
|
|
setStatus('[data-album-photo-status]', '照片已添加');
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
setStatus(
|
|
'[data-album-photo-status]',
|
|
isForbidden(error) ? '当前账号无权添加照片。' : (error.message || '照片添加失败'),
|
|
getApiStateType(error)
|
|
);
|
|
} finally {
|
|
setWritePending(false);
|
|
}
|
|
}
|
|
|
|
async function deleteAlbum(albumId) {
|
|
var api = getApi();
|
|
var genealogyId = requireGenealogyId();
|
|
var album = albumsById[albumId] || currentAlbum;
|
|
|
|
if (writePending || !canEditContent || !genealogyId || !normalizeId(albumId) || redirectUnauthorized(api)) return;
|
|
if (!await confirmAction('确认删除“' + (album ? album.albumName : '该相册') + '”及其中全部照片吗?删除后不可恢复。')) return;
|
|
setWritePending(true);
|
|
try {
|
|
await api.deleteAlbum(genealogyId, albumId);
|
|
if (query('[data-album-page]')) await loadAlbums();
|
|
else if (root.location) root.location.href = buildListUrl(genealogyId);
|
|
} catch (error) {
|
|
if (redirectUnauthorized(api, error)) return;
|
|
showMessage(isForbidden(error) ? '当前账号无权删除该相册。' : (error.message || '相册删除失败'));
|
|
} finally {
|
|
setWritePending(false);
|
|
}
|
|
}
|
|
|
|
async function deletePhoto(photoId) {
|
|
var api = getApi();
|
|
var genealogyId = requireGenealogyId();
|
|
var albumId = getCurrentAlbumId();
|
|
var photo = photosById[photoId];
|
|
|
|
if (writePending || !canEditContent || !genealogyId || !albumId || !normalizeId(photoId) || redirectUnauthorized(api)) return;
|
|
if (!await confirmAction('确认删除“' + (photo && photo.photoTitle || '该照片') + '”吗?删除后不可恢复。')) return;
|
|
setWritePending(true);
|
|
try {
|
|
await api.deleteAlbumPhoto(genealogyId, albumId, photoId);
|
|
await loadPhotos(api, genealogyId, albumId);
|
|
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 albumDelete = event.target.closest('[data-album-delete-id]');
|
|
var photoDelete = event.target.closest('[data-album-photo-delete-id]');
|
|
|
|
if (albumDelete) {
|
|
event.preventDefault();
|
|
deleteAlbum(albumDelete.getAttribute('data-album-delete-id'));
|
|
return;
|
|
}
|
|
if (photoDelete) {
|
|
event.preventDefault();
|
|
deletePhoto(photoDelete.getAttribute('data-album-photo-delete-id'));
|
|
}
|
|
});
|
|
documentRef.addEventListener('submit', function (event) {
|
|
var albumForm = event.target.closest('[data-album-form]');
|
|
var photoForm = event.target.closest('[data-album-photo-form]');
|
|
|
|
if (albumForm) {
|
|
event.preventDefault();
|
|
submitAlbum(albumForm);
|
|
} else if (photoForm) {
|
|
event.preventDefault();
|
|
submitPhoto(photoForm);
|
|
}
|
|
});
|
|
}
|
|
|
|
function init() {
|
|
if (!documentRef) return;
|
|
bindActions();
|
|
if (query('[data-album-page]')) loadAlbums();
|
|
if (query('[data-album-edit-page]')) loadAlbumEditor();
|
|
if (query('[data-album-detail-page]')) loadAlbumDetailPage();
|
|
}
|
|
|
|
return {
|
|
getCurrentGenealogyId: getCurrentGenealogyId,
|
|
getCurrentAlbumId: getCurrentAlbumId,
|
|
buildAlbumBody: buildAlbumBody,
|
|
validateAlbumBody: validateAlbumBody,
|
|
buildAlbumPhotoBody: buildAlbumPhotoBody,
|
|
validateAlbumPhotoBody: validateAlbumPhotoBody,
|
|
canEditAlbums: canEditAlbums,
|
|
normalizeAlbum: normalizeAlbum,
|
|
normalizeAlbumPhoto: normalizeAlbumPhoto,
|
|
normalizeAlbums: normalizeAlbums,
|
|
normalizeAlbumPhotos: normalizeAlbumPhotos,
|
|
findAlbumById: findAlbumById,
|
|
renderAlbumDetail: renderAlbumDetail,
|
|
renderPhotoList: renderPhotoList,
|
|
shouldRedirectToLogin: shouldRedirectToLogin,
|
|
isForbidden: isForbidden,
|
|
init: init
|
|
};
|
|
});
|