Files
mama-box/js/cm-pics.js

97 lines
6.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 11-05 评论 / 回复带图(用户:「评论的时候,或者回复别人评论时候,可以加上上传图片功能」)
孕妈广场帖子详情(guangchang/index.html)和专题页评论区(topic/_shared/tools.js)共用。
· 每条最多 3 张;选图后先在本机压到长边 1600、JPEG 0.85,再传 /api/upload(同投稿),拿回本站地址随评论提交。
· 后端 /plaza/comment 带 images[](只认本站 /api/upload 地址),图片和文字一起进后台「评论审核」,通过后大家才看得到。
· 显示:评论文字下面一排小方图(最多 3 张),点开全屏看大图,点任意处关闭。
用法:
var P = MBCmPics.picker({ strip: 放缩略图的元素, onChange: fn })
P.pick() 打开相册 · P.urls() 已传好的地址 · P.busy() 还有没传完的 · P.count() 已选张数 · P.clear() 清空
MBCmPics.html(images) → 评论里那排图的 HTML(没有图返回 '') */
(function () {
if (window.MBCmPics) return;
var MAX = 3;
var st = document.createElement('style');
st.textContent =
'.cmp-strip{display:flex;gap:8px;padding:8px 2px 2px;overflow-x:auto}.cmp-strip:empty{display:none}' +
'.cmp-t{position:relative;flex:none;width:58px;height:58px;border-radius:10px;overflow:hidden;background:#F1E9E5}' +
'.cmp-t img{width:100%;height:100%;object-fit:cover;display:block}' +
'.cmp-t.up::after{content:"上传中";position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.38);color:#fff;font-size:11px}' +
'.cmp-t.bad::after{content:"失败";position:absolute;inset:0;display:flex;align-items:center;justify-content:center;background:rgba(200,40,40,.5);color:#fff;font-size:11px}' +
'.cmp-t button{position:absolute;right:2px;top:2px;z-index:1;width:18px;height:18px;border-radius:50%;background:rgba(0,0,0,.55);color:#fff;font-size:13px;line-height:18px;text-align:center;padding:0;border:0}' +
'.cmp-g{display:flex;gap:6px;margin-top:6px}' +
'.cmp-g img{width:76px;height:76px;border-radius:8px;object-fit:cover;background:#F1E9E5;cursor:zoom-in}' +
'.cmp-btn{flex:none;display:inline-flex;align-items:center;justify-content:center;width:30px;height:30px;padding:0;border:0;border-radius:50%;background:none;color:#9C8781}' +
'.cmp-btn svg{width:21px;height:21px}' +
'.cmp-btn.on{color:#F4593C}' +
'.cmp-lb{position:fixed;inset:0;z-index:10000;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,.92)}' +
'.cmp-lb img{max-width:100%;max-height:100%;object-fit:contain}';
(document.head || document.documentElement).appendChild(st);
var ICON = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="5" width="18" height="15" rx="3"/><circle cx="9" cy="10.5" r="1.8"/><path d="M21 16l-5-5-8 8"/></svg>';
var esc = function (s) { return String(s).replace(/[&<>"']/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]; }); };
function shrink(f) {
return new Promise(function (res) {
var u = URL.createObjectURL(f), im = new Image();
im.onload = function () {
var k = Math.min(1, 1600 / Math.max(im.width, im.height)), c = document.createElement('canvas');
c.width = Math.round(im.width * k); c.height = Math.round(im.height * k);
c.getContext('2d').drawImage(im, 0, 0, c.width, c.height); URL.revokeObjectURL(u);
c.toBlob(function (b) { res(b || f); }, 'image/jpeg', .85);
};
im.onerror = function () { URL.revokeObjectURL(u); res(f); };
im.src = u;
});
}
function picker(o) {
var pics = [], ups = 0, strip = o.strip;
var fi = document.createElement('input'); fi.type = 'file'; fi.accept = 'image/*'; fi.multiple = true; fi.hidden = true;
document.body.appendChild(fi);
var ch = function () { if (o.onChange) o.onChange(); };
fi.addEventListener('change', function () {
var fs = [].slice.call(fi.files || []); fi.value = '';
if (pics.length + fs.length > MAX && o.toast) o.toast('每条最多带 ' + MAX + ' 张图');
fs.slice(0, MAX - pics.length).forEach(function (f) {
var it = { url: null, el: document.createElement('span') };
it.el.className = 'cmp-t up';
it.el.innerHTML = '<img alt=""><button type="button" aria-label="删除这张">×</button>';
it.el.querySelector('img').src = URL.createObjectURL(f);
it.el.querySelector('button').addEventListener('mousedown', function (e) { e.preventDefault(); });
it.el.querySelector('button').onclick = function () { pics = pics.filter(function (x) { return x !== it; }); it.el.remove(); ch(); };
strip.appendChild(it.el); pics.push(it); ups++;
shrink(f).then(function (b) { var fd = new FormData(); fd.append('file', b, 'comment.jpg'); return fetch('/api/upload', { method: 'POST', body: fd }); })
.then(function (r) { return r.json(); }).then(function (j) { it.url = j && j.result && j.result.url || null; if (!it.url) throw 0; it.el.classList.remove('up'); })
.catch(function () { if (o.toast) o.toast('图片上传失败,可以删掉重新选'); it.el.classList.remove('up'); it.el.classList.add('bad'); })
.then(function () { ups--; ch(); });
});
ch();
});
return {
pick: function () { if (pics.length >= MAX) { if (o.toast) o.toast('每条最多带 ' + MAX + ' 张图'); return; } fi.click(); },
urls: function () { return pics.map(function (x) { return x.url; }).filter(Boolean); },
busy: function () { return ups > 0; },
count: function () { return pics.length; },
clear: function () { pics = []; strip.innerHTML = ''; ch(); }
};
}
function html(imgs) {
imgs = (imgs || []).filter(function (u) { return typeof u === 'string' && /^(https?:)?\/\//.test(u) || /^\//.test(u); }).slice(0, MAX);
return imgs.length ? '<div class="cmp-g">' + imgs.map(function (u) { return '<img src="' + esc(u) + '" alt="" loading="lazy" data-cmp="' + esc(u) + '">'; }).join('') + '</div>' : '';
}
/* 点评论里的图 → 全屏大图;点任意处关 */
document.addEventListener('click', function (e) {
var im = e.target.closest && e.target.closest('[data-cmp]');
if (!im) return;
e.preventDefault(); e.stopPropagation();
var lb = document.createElement('div'); lb.className = 'cmp-lb';
lb.innerHTML = '<img alt="" src="' + esc(im.dataset.cmp) + '">';
lb.addEventListener('click', function () { lb.remove(); });
document.body.appendChild(lb);
}, true);
window.MBCmPics = { picker: picker, html: html, ICON: ICON, MAX: MAX };
})();