// 文章附件区共享逻辑(管理员与普通用户的文章新建/编辑页共用): // 上传(multipart → 附件接口,写 files 表 type=attachments)、编辑页回填列表、 // 插入正文(图片 ![]() / 其他 []())、图片一键设封面、删除(引用计数由服务端处理)。 // // 由页面调用:initArticleAttachments(cfg) // cfg: // uploadURL 上传接口,如 "/api/my/articles/attachments"(DELETE 为 uploadURL + "/") // listURL 列表接口模板,":id" 会被替换为文章 id,如 "/api/my/articles/:id/attachments" // editor EasyMDE 实例(用于在光标处插入 Markdown) // articleID 文章 id(编辑页);新建页为 0 // sessionToken 新建页的临时归属令牌 // texts 页面 i18n 文案:{pick, uploading, insert, setCover, coverSet, del, delConfirm, err} window.initArticleAttachments = function (cfg) { var uploadBtn = document.getElementById('attachmentUploadBtn'); var fileInput = document.getElementById('attachmentInput'); var msgEl = document.getElementById('attachmentMsg'); var listEl = document.getElementById('attachmentList'); var texts = cfg.texts || {}; if (!uploadBtn || !listEl || !fileInput) { return; } var meta = document.querySelector('meta[name="csrf-token"]'); var csrfToken = meta ? meta.getAttribute('content') : ''; // Enable the uploader (uploads allowed only while logged in, which is true here). uploadBtn.disabled = false; fileInput.disabled = false; function fmtSize(b) { if (b < 1024) { return b + ' B'; } var u = ['KiB', 'MiB', 'GiB'], i = -1; do { b /= 1024; i++; } while (b >= 1024 && i < u.length - 1); return b.toFixed(1) + ' ' + u[i]; } function insertMd(md) { var cm = cfg.editor && cfg.editor.codemirror; if (!cm) { return; } cm.replaceSelection(md + '\n'); cm.focus(); } function addRow(att) { var tr = document.createElement('tr'); tr.className = 'hover:bg-gray-50'; tr.dataset.id = att.id; var nameTd = document.createElement('td'); nameTd.className = 'px-3 py-2 text-sm text-gray-800'; var link = document.createElement('a'); link.href = att.url; link.target = '_blank'; link.textContent = att.filename; nameTd.appendChild(link); var sizeTd = document.createElement('td'); sizeTd.className = 'px-3 py-2 text-sm text-gray-500'; sizeTd.textContent = fmtSize(att.size); var actTd = document.createElement('td'); actTd.className = 'px-3 py-2 text-sm text-right whitespace-nowrap'; var insBtn = document.createElement('button'); insBtn.type = 'button'; insBtn.textContent = texts.insert || 'Insert'; insBtn.className = 'text-blue-600 hover:text-blue-800 font-medium mr-3 cursor-pointer bg-transparent border-none'; insBtn.onclick = function () { var md = att.is_image ? '![' + att.filename + '](' + att.url + ')' : '[' + att.filename + '](' + att.url + ')'; insertMd(md); }; // Images: extra button to copy the URL into the cover field. var coverBtn = null; if (att.is_image) { coverBtn = document.createElement('button'); coverBtn.type = 'button'; coverBtn.textContent = texts.setCover || 'Cover'; coverBtn.className = 'text-green-600 hover:text-green-800 font-medium mr-3 cursor-pointer bg-transparent border-none'; coverBtn.onclick = function () { var cover = document.querySelector('input[name="cover"]'); if (cover) { cover.value = att.url; msgEl.textContent = texts.coverSet || ''; } }; } var delBtn = document.createElement('button'); delBtn.type = 'button'; delBtn.textContent = texts.del || 'Delete'; delBtn.className = 'text-red-600 hover:text-red-800 font-medium cursor-pointer bg-transparent border-none'; delBtn.onclick = function () { if (!confirm(texts.delConfirm || 'Delete?')) { return; } fetch(cfg.uploadURL + '/' + att.id, { method: 'DELETE', headers: { 'X-CSRF-Token': csrfToken } }) .then(function (r) { return r.json(); }) .then(function (r) { if (r.ok) { tr.remove(); } else { msgEl.textContent = r.error || 'error'; } }); }; actTd.appendChild(insBtn); if (coverBtn) { actTd.appendChild(coverBtn); } actTd.appendChild(delBtn); tr.appendChild(nameTd); tr.appendChild(sizeTd); tr.appendChild(actTd); listEl.appendChild(tr); } uploadBtn.addEventListener('click', function () { if (!fileInput.files.length) { msgEl.textContent = texts.pick || 'Pick a file.'; return; } var fd = new FormData(); fd.append('file', fileInput.files[0]); if (cfg.articleID) { fd.append('article_id', cfg.articleID); } else if (cfg.sessionToken) { fd.append('session_token', cfg.sessionToken); } msgEl.textContent = texts.uploading || 'Uploading...'; fetch(cfg.uploadURL, { method: 'POST', headers: { 'X-CSRF-Token': csrfToken }, body: fd }) .then(function (r) { return r.json(); }) .then(function (r) { if (r.error) { msgEl.textContent = r.error; return; } msgEl.textContent = ''; addRow(r); fileInput.value = ''; }) .catch(function () { msgEl.textContent = texts.err || 'Upload failed.'; }); }); // Edit page: load existing attachments. if (cfg.articleID) { fetch(cfg.listURL.replace(':id', cfg.articleID)) .then(function (r) { return r.json(); }) .then(function (r) { (r.attachments || []).forEach(addRow); }); } };