修改完成
This commit is contained in:
+972
-3
File diff suppressed because it is too large
Load Diff
@@ -1263,3 +1263,34 @@
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
||||
.attachment-edit-list {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(180px, 100%), 220px));
|
||||
gap: 12px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.attachment-edit-list:empty { display: none; }
|
||||
|
||||
.attachment-edit-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
min-width: 0;
|
||||
padding: 10px;
|
||||
border: 1px solid var(--border, #e6ddcf);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.attachment-edit-item :is(img, video) {
|
||||
width: 100%;
|
||||
height: 140px;
|
||||
object-fit: contain;
|
||||
background: var(--paper, #faf7f0);
|
||||
}
|
||||
|
||||
.attachment-edit-name { overflow-wrap: anywhere; font-size: 13px; }
|
||||
.attachment-edit-remove { margin-top: auto; }
|
||||
.attachment-edit-remove:focus-visible { outline: 2px solid currentColor; outline-offset: 3px; }
|
||||
.attachment-edit-remove:disabled { opacity: .5; cursor: not-allowed; }
|
||||
|
||||
@@ -1425,9 +1425,9 @@ textarea {
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
*:not(:where(.page-home-portal, .page-home-portal *)),
|
||||
*:not(:where(.page-home-portal, .page-home-portal *))::before,
|
||||
*:not(:where(.page-home-portal, .page-home-portal *))::after {
|
||||
animation-duration: .01ms !important;
|
||||
animation-iteration-count: 1 !important;
|
||||
scroll-behavior: auto !important;
|
||||
|
||||
@@ -510,7 +510,6 @@
|
||||
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');
|
||||
}
|
||||
@@ -537,6 +536,8 @@
|
||||
album = findAlbumById(albums, albumId);
|
||||
if (!album) throw new Error('相册列表未返回当前相册');
|
||||
fillAlbumForm(album);
|
||||
var albumResponse = albums.find(function (entry) { return String(entry.albumId) === albumId; });
|
||||
root.AttachmentEditor.setFiles('#albumCoverOssId', albumResponse.coverFile ? [albumResponse.coverFile] : []);
|
||||
if (query('[data-album-editor-title]')) query('[data-album-editor-title]').textContent = '编辑相册';
|
||||
}
|
||||
setEditorEnabled(true);
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
var sortOrder = optionalSafeInteger(source.sortOrder);
|
||||
|
||||
if (articleSummary !== undefined) body.articleSummary = articleSummary;
|
||||
if (source.coverOssId === null) body.coverOssId = null;
|
||||
if (source.coverOssId !== undefined && source.coverOssId !== null && source.coverOssId !== '') {
|
||||
if (coverOssId) body.coverOssId = coverOssId;
|
||||
else body.invalidCoverOssId = true;
|
||||
@@ -446,7 +447,7 @@
|
||||
var values = {};
|
||||
|
||||
queryAll('[name]', form).forEach(function (field) {
|
||||
values[field.name] = field.value;
|
||||
values[field.name] = root.AttachmentEditor ? root.AttachmentEditor.readField(field) : field.value;
|
||||
});
|
||||
return values;
|
||||
}
|
||||
@@ -464,7 +465,6 @@
|
||||
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);
|
||||
@@ -496,9 +496,11 @@
|
||||
await loadCapability(api, genealogyId);
|
||||
if (!canEditContent) throw { status: 403, message: '当前账号无权维护该家谱的谱文。' };
|
||||
if (articleId) {
|
||||
article = normalizeArticle(await api.articleDetail(genealogyId, articleId));
|
||||
var articleResponse = await api.articleDetail(genealogyId, articleId);
|
||||
article = normalizeArticle(articleResponse);
|
||||
if (!article || article.articleId !== articleId) throw new Error('谱文详情响应缺少稳定 ArticleVo 字段');
|
||||
fillArticleForm(article);
|
||||
root.AttachmentEditor.setFiles('#articleCoverOssId', articleResponse.coverFile ? [articleResponse.coverFile] : []);
|
||||
if (query('[data-article-editor-title]')) query('[data-article-editor-title]').textContent = '编辑谱文';
|
||||
}
|
||||
setEditorEnabled(true);
|
||||
|
||||
@@ -0,0 +1,182 @@
|
||||
(function (root, factory) {
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
module.exports = factory(root);
|
||||
return;
|
||||
}
|
||||
root.AttachmentEditor = factory(root);
|
||||
})(typeof globalThis !== 'undefined' ? globalThis : window, function (root) {
|
||||
'use strict';
|
||||
|
||||
var editors = new Map();
|
||||
|
||||
function normalizeFiles(files) {
|
||||
var seen = new Set();
|
||||
return (files || []).map(function (file) {
|
||||
var ossId = file && file.ossId;
|
||||
if ((typeof ossId === 'number' && !Number.isSafeInteger(ossId)) || !/^[1-9][0-9]*$/.test(String(ossId))) {
|
||||
throw new Error('原附件缺少有效的上传标识,暂不能保存,请刷新后重试');
|
||||
}
|
||||
return {
|
||||
ossId: String(ossId),
|
||||
fileName: String(file.fileName || '附件'),
|
||||
mediaType: String(file.mediaType || '').toLowerCase(),
|
||||
accessUrl: root.MediaDisplay ? (root.MediaDisplay.normalizeFileAccess(file) || {}).accessUrl || '' : ''
|
||||
};
|
||||
}).filter(function (file) {
|
||||
if (seen.has(file.ossId)) return false;
|
||||
seen.add(file.ossId);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function release(files) {
|
||||
files.forEach(function (file) {
|
||||
if (file.objectUrl) root.URL.revokeObjectURL(file.objectUrl);
|
||||
});
|
||||
}
|
||||
|
||||
function getEditor(target) {
|
||||
if (typeof target === 'string') target = root.document.querySelector(target);
|
||||
return editors.get(target);
|
||||
}
|
||||
|
||||
function sync(editor) {
|
||||
editor.target.value = editor.files.map(function (file) { return file.ossId; }).join(',');
|
||||
editor.preview.replaceChildren();
|
||||
editor.files.forEach(function (file) {
|
||||
var card = root.document.createElement('div');
|
||||
var url = file.objectUrl || file.accessUrl;
|
||||
var name = root.document.createElement('span');
|
||||
var remove = root.document.createElement('button');
|
||||
var media;
|
||||
card.className = 'attachment-edit-item';
|
||||
if (url && /^(image|video)(\/|$)/.test(file.mediaType)) {
|
||||
media = root.document.createElement(/^image/.test(file.mediaType) ? 'img' : 'video');
|
||||
media.src = url;
|
||||
if (media.tagName === 'IMG') media.alt = file.fileName;
|
||||
else { media.controls = true; media.preload = 'metadata'; }
|
||||
media.addEventListener('error', function () {
|
||||
media.hidden = true;
|
||||
name.textContent = file.fileName + '(预览不可用,附件仍保留)';
|
||||
});
|
||||
card.appendChild(media);
|
||||
}
|
||||
name.className = 'attachment-edit-name';
|
||||
name.textContent = file.fileName;
|
||||
card.appendChild(name);
|
||||
remove.type = 'button';
|
||||
remove.className = 'pill attachment-edit-remove';
|
||||
remove.textContent = '移除';
|
||||
remove.setAttribute('aria-label', '移除 ' + file.fileName);
|
||||
remove.disabled = editor.busy || editor.input.disabled;
|
||||
// 相册接口尚不支持可靠清空封面,仅允许替换。
|
||||
if (editor.input.hasAttribute('data-attachment-replace-only')) {
|
||||
remove.hidden = true;
|
||||
}
|
||||
remove.addEventListener('click', function () {
|
||||
if (editor.busy || editor.input.disabled) return;
|
||||
editor.files = editor.files.filter(function (existing) { return existing !== file; });
|
||||
release([file]);
|
||||
editor.cleared = true;
|
||||
sync(editor);
|
||||
});
|
||||
card.appendChild(remove);
|
||||
editor.preview.appendChild(card);
|
||||
});
|
||||
if (editor.status && !editor.busy) {
|
||||
editor.status.textContent = editor.files.length
|
||||
? '已选择 ' + editor.files.length + ' 个文件;移除或替换将在保存后生效'
|
||||
: (editor.cleared ? '附件已移除,保存后生效' : '未选择文件');
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
root.document.querySelectorAll('[data-attachment-editor]').forEach(function (input) {
|
||||
var target = root.document.querySelector(input.getAttribute('data-upload-target'));
|
||||
var preview;
|
||||
var editor;
|
||||
if (!target || editors.has(target)) return;
|
||||
preview = root.document.createElement('div');
|
||||
preview.className = 'attachment-edit-list';
|
||||
preview.setAttribute('aria-label', '已选择的附件');
|
||||
input.insertAdjacentElement('afterend', preview);
|
||||
editor = {
|
||||
target: target, input: input, preview: preview,
|
||||
status: root.document.querySelector(input.getAttribute('data-upload-status')),
|
||||
multiple: input.getAttribute('data-upload-multiple') === 'true',
|
||||
files: [], cleared: false, busy: false
|
||||
};
|
||||
editors.set(target, editor);
|
||||
new root.MutationObserver(function () {
|
||||
preview.querySelectorAll('button').forEach(function (button) { button.disabled = input.disabled || editor.busy; });
|
||||
}).observe(input, { attributes: true, attributeFilter: ['disabled'] });
|
||||
});
|
||||
}
|
||||
|
||||
function setFiles(target, files) {
|
||||
init();
|
||||
var editor = getEditor(target);
|
||||
if (!editor) throw new Error('附件编辑控件未初始化');
|
||||
editor.invalid = true;
|
||||
var normalized = normalizeFiles(files);
|
||||
release(editor.files);
|
||||
editor.files = normalized;
|
||||
editor.cleared = false;
|
||||
editor.invalid = false;
|
||||
sync(editor);
|
||||
}
|
||||
|
||||
function addUploaded(target, upload, file) {
|
||||
var editor = getEditor(target);
|
||||
if (!editor) return false;
|
||||
var attachment = normalizeFiles([{ ossId: upload.ossId, fileName: file.name, mediaType: file.type }])[0];
|
||||
if (editor.files.some(function (existing) { return existing.ossId === attachment.ossId; })) return true;
|
||||
attachment.objectUrl = root.URL.createObjectURL(file);
|
||||
if (!editor.multiple) { release(editor.files); editor.files = []; }
|
||||
editor.files.push(attachment);
|
||||
editor.cleared = false;
|
||||
sync(editor);
|
||||
return true;
|
||||
}
|
||||
|
||||
function clear(target) {
|
||||
var editor = getEditor(target);
|
||||
if (!editor || editor.busy || editor.input.disabled) return;
|
||||
release(editor.files);
|
||||
editor.files = [];
|
||||
editor.cleared = true;
|
||||
sync(editor);
|
||||
}
|
||||
|
||||
function readField(field) {
|
||||
var editor = getEditor(field);
|
||||
// nullable 单封面 PATCH:空值仅在用户主动移除时提交 null。
|
||||
if (editor && !editor.multiple && editor.cleared && !editor.files.length) return null;
|
||||
return field.value;
|
||||
}
|
||||
|
||||
function setBusy(target, busy) {
|
||||
var editor = getEditor(target);
|
||||
if (!editor) return;
|
||||
editor.busy = busy;
|
||||
editor.preview.querySelectorAll('button').forEach(function (button) { button.disabled = busy || editor.input.disabled; });
|
||||
}
|
||||
|
||||
if (root.document) {
|
||||
root.document.addEventListener('DOMContentLoaded', init);
|
||||
root.document.addEventListener('submit', function (event) {
|
||||
var pending = Array.from(editors.values()).some(function (editor) { return editor.input.form === event.target && editor.busy; });
|
||||
var invalid = Array.from(editors.values()).some(function (editor) { return editor.input.form === event.target && editor.invalid; });
|
||||
if (!pending && !invalid) return;
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
var status = event.target.querySelector('.upload-status');
|
||||
if (status) status.textContent = invalid ? '原附件读取不完整,请刷新后重试,暂不能保存' : '文件正在上传,请等待上传完成后再保存';
|
||||
}, true);
|
||||
root.addEventListener('pagehide', function (event) {
|
||||
if (!event.persisted) editors.forEach(function (editor) { release(editor.files); });
|
||||
});
|
||||
}
|
||||
|
||||
return { init: init, normalizeFiles: normalizeFiles, setFiles: setFiles, addUploaded: addUploaded, clear: clear, readField: readField, setBusy: setBusy };
|
||||
});
|
||||
@@ -166,6 +166,7 @@
|
||||
if (latitude !== undefined) body.latitude = latitude;
|
||||
else body.invalidLatitude = true;
|
||||
}
|
||||
if (source.coverOssId === null) body.coverOssId = null;
|
||||
if (source.coverOssId !== undefined && source.coverOssId !== null && source.coverOssId !== '') {
|
||||
if (coverOssId) body.coverOssId = coverOssId;
|
||||
else body.invalidCoverOssId = true;
|
||||
@@ -615,7 +616,7 @@
|
||||
function getFormValues(form) {
|
||||
var values = {};
|
||||
|
||||
queryAll('[name]', form).forEach(function (field) { values[field.name] = field.value; });
|
||||
queryAll('[name]', form).forEach(function (field) { values[field.name] = root.AttachmentEditor ? root.AttachmentEditor.readField(field) : field.value; });
|
||||
return values;
|
||||
}
|
||||
|
||||
@@ -630,7 +631,7 @@
|
||||
var contentEditor;
|
||||
|
||||
if (!ceremony || ceremony.status !== '0') throw new Error('停用活动无法通过当前 PC 接口重新读取或编辑');
|
||||
['ceremonyType', 'ceremonyTitle', 'ceremonyDesc', 'location', 'locationAddress', 'longitude', 'latitude', 'coverOssId', 'sortOrder'].forEach(function (name) {
|
||||
['ceremonyType', 'ceremonyTitle', 'ceremonyDesc', 'location', 'locationAddress', 'longitude', 'latitude', 'sortOrder'].forEach(function (name) {
|
||||
setFieldValue('[data-ceremony-form]', name, ceremony[name]);
|
||||
});
|
||||
setFieldValue('[data-ceremony-form]', 'ceremonyTime', toDateTimeInputValue(ceremony.ceremonyTime));
|
||||
@@ -662,9 +663,11 @@
|
||||
await loadCapability(api, genealogyId);
|
||||
if (!canEditContent) throw { status: 403, message: '当前账号无权维护该家谱的祭祀活动。' };
|
||||
if (ceremonyId) {
|
||||
ceremony = normalizeCeremony(await api.ceremonyDetail(genealogyId, ceremonyId));
|
||||
var ceremonyResponse = await api.ceremonyDetail(genealogyId, ceremonyId);
|
||||
ceremony = normalizeCeremony(ceremonyResponse);
|
||||
if (!ceremony || ceremony.ceremonyId !== ceremonyId) throw new Error('活动详情响应缺少稳定 ceremonyId');
|
||||
fillCeremonyForm(ceremony);
|
||||
root.AttachmentEditor.setFiles('#giftCoverOssId', ceremonyResponse.coverFile ? [ceremonyResponse.coverFile] : []);
|
||||
if (query('[data-ceremony-editor-title]')) query('[data-ceremony-editor-title]').textContent = '编辑祭祀活动';
|
||||
}
|
||||
setEditorEnabled(true);
|
||||
|
||||
+13
-1
@@ -302,7 +302,7 @@
|
||||
if (trimOrUndefined(source.sortOrder) !== undefined && sortOrder === undefined) {
|
||||
throw new Error('排序值必须是非负整数');
|
||||
}
|
||||
if (mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds;
|
||||
if (source.mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds || '';
|
||||
if (status !== undefined) body.status = status;
|
||||
if (sortOrder !== undefined) body.sortOrder = sortOrder;
|
||||
return body;
|
||||
@@ -618,11 +618,22 @@
|
||||
|
||||
function fillFeedForm(data) {
|
||||
var feed = normalizeFeed(data);
|
||||
var contentField;
|
||||
var contentEditor;
|
||||
|
||||
if (!feed) throw new Error('动态响应缺少 feedId 或 feedContent,请联系后端补充 DTO。');
|
||||
queryAll('[data-feed-form] [name]').forEach(function (field) {
|
||||
if (feed[field.name] !== undefined && feed[field.name] !== null) field.value = feed[field.name];
|
||||
});
|
||||
contentField = query('[data-feed-form] [name="feedContent"]');
|
||||
if (contentField && root.AppRichEditor && root.AppRichEditor.init) {
|
||||
contentEditor = root.AppRichEditor.init(contentField);
|
||||
if (contentEditor && contentEditor.editor && contentEditor.editor.setHtml) {
|
||||
contentEditor.editor.setHtml(feed.feedContent);
|
||||
contentEditor.sync();
|
||||
}
|
||||
}
|
||||
root.AttachmentEditor.setFiles('#feedMedia', data.mediaFiles || []);
|
||||
}
|
||||
|
||||
function canEditFeeds(genealogy) {
|
||||
@@ -788,6 +799,7 @@
|
||||
setFeedFormStatus('');
|
||||
} catch (error) {
|
||||
if (redirectUnauthorized(api, error)) return;
|
||||
setFeedFormEnabled(false);
|
||||
setFeedFormStatus(
|
||||
getApiStateType(error) === 'forbidden' ? '当前账号无权维护该家谱的动态。' : (error.message || '动态详情加载失败'),
|
||||
getApiStateType(error)
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
if (recordContent !== undefined) body.recordContent = recordContent;
|
||||
if (recordDate !== undefined) body.recordDate = recordDate;
|
||||
if (remindTime !== undefined) body.remindTime = remindTime;
|
||||
if (mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds;
|
||||
if (source.mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds || '';
|
||||
if (sortOrderText !== undefined) {
|
||||
body.sortOrder = toSafeInteger(sortOrderText);
|
||||
if (body.sortOrder === undefined) body.invalidSortOrder = true;
|
||||
@@ -136,7 +136,7 @@
|
||||
function validateGrowthRecordBody(body) {
|
||||
if (!body || !body.recordTitle) return '请填写记录标题';
|
||||
if (body.lineagePersonId !== undefined && !normalizeId(body.lineagePersonId)) return '请选择有效的世系人物';
|
||||
if (body.mediaOssIds !== undefined && !/^[1-9][0-9]*(,[1-9][0-9]*)*$/.test(body.mediaOssIds)) {
|
||||
if (body.mediaOssIds !== undefined && body.mediaOssIds !== '' && !/^[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))) {
|
||||
@@ -554,7 +554,6 @@
|
||||
setFieldValue('recordContent', record.recordContent);
|
||||
setFieldValue('recordDate', toDateInputValue(record.recordDate));
|
||||
setFieldValue('remindTime', toDateTimeInputValue(record.remindTime));
|
||||
setFieldValue('mediaOssIds', record.mediaOssIds);
|
||||
setFieldValue('sortOrder', record.sortOrder);
|
||||
setFieldValue('status', '0');
|
||||
contentField = query('[data-growth-form] [name="recordContent"]');
|
||||
@@ -564,11 +563,6 @@
|
||||
editorInstance.editor.setHtml(record.recordContent || '');
|
||||
}
|
||||
}
|
||||
if (query('[data-growth-media-status]')) {
|
||||
query('[data-growth-media-status]').textContent = record.mediaOssIds
|
||||
? '已保留 ' + attachmentCount(record.mediaOssIds) + ' 个附件,可继续选择追加'
|
||||
: '未选择文件';
|
||||
}
|
||||
}
|
||||
|
||||
function applyLineageOptions(data, selectedId, fallbackName) {
|
||||
@@ -603,6 +597,7 @@
|
||||
applyLineageOptions(result[0], record && record.lineagePersonId, record && record.lineagePersonName);
|
||||
if (record) {
|
||||
fillGrowthForm(record);
|
||||
root.AttachmentEditor.setFiles('#growthMediaOssIds', result[1].mediaFiles || []);
|
||||
if (query('[data-growth-editor-title]')) query('[data-growth-editor-title]').textContent = '编辑成长记录';
|
||||
}
|
||||
setEditorEnabled(true);
|
||||
@@ -683,13 +678,7 @@
|
||||
}
|
||||
|
||||
function clearMediaSelection() {
|
||||
var target = query('[data-growth-form] [name="mediaOssIds"]');
|
||||
var status = query('[data-growth-media-status]');
|
||||
var fileInput = query('[data-growth-media-input]');
|
||||
|
||||
if (target) target.value = '';
|
||||
if (fileInput) fileInput.value = '';
|
||||
if (status) status.textContent = '未选择文件';
|
||||
root.AttachmentEditor.clear('#growthMediaOssIds');
|
||||
}
|
||||
|
||||
function bindActions() {
|
||||
|
||||
+6
-16
@@ -110,7 +110,7 @@
|
||||
if (memoContent !== undefined) body.memoContent = memoContent;
|
||||
if (remindTime !== undefined) body.remindTime = remindTime;
|
||||
if (completed !== undefined) body.completed = completed;
|
||||
if (mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds;
|
||||
if (source.mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds || '';
|
||||
if (sortOrderText !== undefined) {
|
||||
body.sortOrder = toSafeInteger(sortOrderText);
|
||||
if (body.sortOrder === undefined) body.invalidSortOrder = true;
|
||||
@@ -141,7 +141,7 @@
|
||||
if (body.completed !== undefined && body.completed !== '0' && body.completed !== '1') {
|
||||
return '完成状态只能是未完成或已完成';
|
||||
}
|
||||
if (body.mediaOssIds !== undefined && !/^[1-9][0-9]*(,[1-9][0-9]*)*$/.test(body.mediaOssIds)) {
|
||||
if (body.mediaOssIds !== undefined && body.mediaOssIds !== '' && !/^[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))) {
|
||||
@@ -518,14 +518,8 @@
|
||||
setFieldValue('memoContent', memo.memoContent);
|
||||
setFieldValue('remindTime', toDateTimeInputValue(memo.remindTime));
|
||||
setFieldValue('completed', memo.completed);
|
||||
setFieldValue('mediaOssIds', memo.mediaOssIds);
|
||||
setFieldValue('sortOrder', memo.sortOrder);
|
||||
setFieldValue('status', '0');
|
||||
if (query('[data-memo-media-status]')) {
|
||||
query('[data-memo-media-status]').textContent = memo.mediaOssIds
|
||||
? '已保留 ' + attachmentCount(memo.mediaOssIds) + ' 个附件,可继续选择追加'
|
||||
: '未选择文件';
|
||||
}
|
||||
}
|
||||
|
||||
async function loadMemoEditor() {
|
||||
@@ -545,9 +539,11 @@
|
||||
await loadCapability(api, genealogyId);
|
||||
if (!canEditContent) throw { status: 403, message: '当前账号无权维护该家谱的备忘录。' };
|
||||
if (memoId) {
|
||||
memo = normalizeMemo(await api.memoDetail(genealogyId, memoId));
|
||||
var memoResponse = await api.memoDetail(genealogyId, memoId);
|
||||
memo = normalizeMemo(memoResponse);
|
||||
if (!memo) throw new Error('备忘录详情响应缺少稳定 MemoVo 字段');
|
||||
fillMemoForm(memo);
|
||||
root.AttachmentEditor.setFiles('#memoMediaOssIds', memoResponse.mediaFiles || []);
|
||||
if (query('[data-memo-editor-title]')) query('[data-memo-editor-title]').textContent = '编辑备忘录';
|
||||
}
|
||||
setEditorEnabled(true);
|
||||
@@ -628,13 +624,7 @@
|
||||
}
|
||||
|
||||
function clearMediaSelection() {
|
||||
var target = query('[data-memo-form] [name="mediaOssIds"]');
|
||||
var status = query('[data-memo-media-status]');
|
||||
var fileInput = query('[data-memo-media-input]');
|
||||
|
||||
if (target) target.value = '';
|
||||
if (fileInput) fileInput.value = '';
|
||||
if (status) status.textContent = '未选择文件';
|
||||
root.AttachmentEditor.clear('#memoMediaOssIds');
|
||||
}
|
||||
|
||||
function bindActions() {
|
||||
|
||||
@@ -252,7 +252,7 @@
|
||||
menu.setAttribute("aria-hidden", "true");
|
||||
|
||||
inner.className = "container site-mobile-menu-inner";
|
||||
[...navLinks.querySelectorAll("a"), ...(navActions ? navActions.querySelectorAll("a") : [])].forEach((link) => {
|
||||
[...navLinks.querySelectorAll("a, button"), ...(navActions ? navActions.querySelectorAll("a") : [])].forEach((link) => {
|
||||
inner.appendChild(link.cloneNode(true));
|
||||
});
|
||||
menu.appendChild(inner);
|
||||
@@ -288,9 +288,10 @@
|
||||
initSiteMenu();
|
||||
refreshPublicUser();
|
||||
|
||||
// 只在精确指针设备上启用微动效,触屏和减少动效偏好下保持静态。
|
||||
// 鼠标反馈仅用于精确指针;首页独立启用动效,其他页面遵循系统偏好。
|
||||
const finePointer = window.matchMedia("(hover: hover) and (pointer: fine)").matches;
|
||||
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
const reduceMotion = !document.body.classList.contains("page-home-portal") &&
|
||||
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||
|
||||
if (!finePointer || reduceMotion) return;
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@
|
||||
else if (!isExactJsonNumber(amountText, body.giftAmount)) body.invalidGiftPrecision = true;
|
||||
}
|
||||
if (recordContent !== undefined) body.recordContent = recordContent;
|
||||
if (mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds;
|
||||
if (source.mediaOssIds !== undefined) body.mediaOssIds = mediaOssIds || '';
|
||||
if (sortOrderText !== undefined) {
|
||||
body.sortOrder = toSafeInteger(sortOrderText);
|
||||
if (body.sortOrder === undefined) body.invalidSortOrder = true;
|
||||
@@ -168,7 +168,7 @@
|
||||
if (body.eventTime !== undefined && !isValidBackendDateTime(body.eventTime)) {
|
||||
return '事件时间格式无效';
|
||||
}
|
||||
if (body.mediaOssIds !== undefined && !/^[1-9][0-9]*(,[1-9][0-9]*)*$/.test(body.mediaOssIds)) {
|
||||
if (body.mediaOssIds !== undefined && body.mediaOssIds !== '' && !/^[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))) {
|
||||
@@ -576,14 +576,8 @@
|
||||
setFieldValue('eventTime', toDateTimeInputValue(record.eventTime));
|
||||
setFieldValue('giftAmount', record.giftAmount);
|
||||
setFieldValue('recordContent', record.recordContent);
|
||||
setFieldValue('mediaOssIds', record.mediaOssIds);
|
||||
setFieldValue('sortOrder', record.sortOrder);
|
||||
setFieldValue('status', '0');
|
||||
if (query('[data-relative-media-status]')) {
|
||||
query('[data-relative-media-status]').textContent = record.mediaOssIds
|
||||
? '已保留 ' + attachmentCount(record.mediaOssIds) + ' 个附件,可继续选择追加'
|
||||
: '未选择文件';
|
||||
}
|
||||
}
|
||||
|
||||
async function loadRelativeEditor() {
|
||||
@@ -603,9 +597,11 @@
|
||||
await loadCapability(api, genealogyId);
|
||||
if (!canEditContent) throw { status: 403, message: '当前账号无权维护该家谱的亲友记录。' };
|
||||
if (relativeId) {
|
||||
record = normalizeRelativeRecord(await api.relativeRecordDetail(genealogyId, relativeId));
|
||||
var relativeResponse = await api.relativeRecordDetail(genealogyId, relativeId);
|
||||
record = normalizeRelativeRecord(relativeResponse);
|
||||
if (!record) throw new Error('亲友记录详情响应缺少稳定 RelativeRecordVo 字段');
|
||||
fillRelativeForm(record);
|
||||
root.AttachmentEditor.setFiles('#relativeMediaOssIds', relativeResponse.mediaFiles || []);
|
||||
if (query('[data-relative-editor-title]')) query('[data-relative-editor-title]').textContent = '编辑亲友记录';
|
||||
}
|
||||
setEditorEnabled(true);
|
||||
@@ -688,13 +684,7 @@
|
||||
}
|
||||
|
||||
function clearMediaSelection() {
|
||||
var target = query('[data-relative-form] [name="mediaOssIds"]');
|
||||
var status = query('[data-relative-media-status]');
|
||||
var fileInput = query('[data-relative-media-input]');
|
||||
|
||||
if (target) target.value = '';
|
||||
if (fileInput) fileInput.value = '';
|
||||
if (status) status.textContent = '未选择文件';
|
||||
root.AttachmentEditor.clear('#relativeMediaOssIds');
|
||||
}
|
||||
|
||||
function bindActions() {
|
||||
|
||||
@@ -225,6 +225,7 @@
|
||||
|
||||
if (!files.length || !target || redirectUnauthorized(api)) return;
|
||||
input.disabled = true;
|
||||
if (root.AttachmentEditor) root.AttachmentEditor.setBusy(target, true);
|
||||
|
||||
try {
|
||||
for (index = 0; index < files.length; index += 1) {
|
||||
@@ -235,7 +236,9 @@
|
||||
}
|
||||
}));
|
||||
if (!result.ossId) throw new Error('上传响应缺少 ossId');
|
||||
target.value = mergeUploadTargetValue(target.value, result.ossId, multiple);
|
||||
if (!root.AttachmentEditor || !root.AttachmentEditor.addUploaded(target, result, files[index])) {
|
||||
target.value = mergeUploadTargetValue(target.value, result.ossId, multiple);
|
||||
}
|
||||
setUploadStatus(status, '', buildUploadStatus(result.fileName || files[index].name, result.ossId));
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -244,6 +247,7 @@
|
||||
showMessage(isForbidden(error) ? '当前账号无权上传文件。' : (error.message || '上传失败'));
|
||||
} finally {
|
||||
input.disabled = false;
|
||||
if (root.AttachmentEditor) root.AttachmentEditor.setBusy(target, false);
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,7 @@
|
||||
|
||||
if (videoDesc !== undefined) body.videoDesc = videoDesc;
|
||||
if (coverOssId !== undefined) body.coverOssId = coverOssId;
|
||||
if (source.coverOssId === null) body.coverOssId = null;
|
||||
if (videoOssId !== undefined) body.videoOssId = videoOssId;
|
||||
if (durationText !== undefined) {
|
||||
body.durationSeconds = toSafeInteger(durationText);
|
||||
@@ -114,7 +115,7 @@
|
||||
if (!body || !body.videoTitle) return '请填写视频标题';
|
||||
if (!body.videoOssId) return '请先选择并上传视频文件';
|
||||
if (!normalizeId(body.videoOssId)) return '视频文件上传结果无效';
|
||||
if (body.coverOssId !== undefined && !normalizeId(body.coverOssId)) return '封面上传结果无效';
|
||||
if (body.coverOssId !== undefined && body.coverOssId !== null && !normalizeId(body.coverOssId)) return '封面上传结果无效';
|
||||
if (body.invalidDuration || (body.durationSeconds !== undefined && !Number.isSafeInteger(body.durationSeconds))) {
|
||||
return '视频时长必须是安全整数';
|
||||
}
|
||||
@@ -488,7 +489,7 @@
|
||||
var values = {};
|
||||
|
||||
queryAll('[name]', form).forEach(function (field) {
|
||||
values[field.name] = field.value;
|
||||
values[field.name] = root.AttachmentEditor ? root.AttachmentEditor.readField(field) : field.value;
|
||||
});
|
||||
return values;
|
||||
}
|
||||
@@ -502,13 +503,9 @@
|
||||
function fillVideoForm(video) {
|
||||
setFieldValue('videoTitle', video.videoTitle);
|
||||
setFieldValue('videoDesc', video.videoDesc);
|
||||
setFieldValue('coverOssId', video.coverOssId);
|
||||
setFieldValue('videoOssId', video.videoOssId);
|
||||
setFieldValue('durationSeconds', video.durationSeconds);
|
||||
setFieldValue('sortOrder', video.sortOrder);
|
||||
setFieldValue('status', '0');
|
||||
if (query('[data-video-file-status]')) query('[data-video-file-status]').textContent = '已保留原视频文件,可重新选择替换';
|
||||
if (query('[data-video-cover-status]')) query('[data-video-cover-status]').textContent = video.coverOssId ? '已保留原封面,可重新选择替换' : '未选择封面';
|
||||
}
|
||||
|
||||
async function loadVideoEditor() {
|
||||
@@ -540,6 +537,8 @@
|
||||
video = normalizeVideo(result[1]);
|
||||
if (!video) throw new Error('视频详情响应缺少稳定 VideoVo 字段');
|
||||
fillVideoForm(video);
|
||||
root.AttachmentEditor.setFiles('#video-oss-id', result[1].videoFile ? [result[1].videoFile] : []);
|
||||
root.AttachmentEditor.setFiles('#video-cover-oss-id', result[1].coverFile ? [result[1].coverFile] : []);
|
||||
if (query('[data-video-editor-title]')) query('[data-video-editor-title]').textContent = '编辑视频';
|
||||
}
|
||||
setFormStatus('');
|
||||
|
||||
Reference in New Issue
Block a user