修改完成
This commit is contained in:
@@ -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 };
|
||||
});
|
||||
Reference in New Issue
Block a user