v26110459 广场帖子 / 专题页评论和回复可带图(最多 3 张,审核后公开)

This commit is contained in:
deploy
2026-11-05 09:50:56 +00:00
parent 4d1e5e95f1
commit 565a13eb60
26 changed files with 261 additions and 127 deletions

View File

@@ -44,6 +44,10 @@
'.tc-in label{flex:1;min-width:0;display:flex;padding:6px 14px;border-radius:20px;background:#F2F3F5}' +
'.tc-in textarea{flex:1;min-width:0;border:0;background:transparent;resize:none;font-size:14px;line-height:1.6;font-family:inherit;color:#1F2329;height:24px;max-height:96px;outline:0;padding:0}' +
'.tc-in button{flex:0 0 auto;height:36px;padding:0 16px;border:0;border-radius:99px;background:var(--tt-c,#18A058);color:#fff;font-size:13.5px;font-weight:700}.tc-in button:disabled{opacity:.35}' +
/* 11-05 评论带图:图片按钮 / 缩略图条(js/cm-pics.js)别吃到上面通用的绿色按钮样式;缩略图条浮在输入条上方,「回复 XX」条出来时再往上让一格 */
'.tc-in .cmp-btn{height:36px;width:32px;padding:0;background:none;color:#86909C;border-radius:50%}.tc-in .cmp-btn.on{color:var(--tt-c,#18A058)}.tc-in .cmp-btn[hidden]{display:none}' +
'.tc-in .cmp-t button{width:18px;height:18px;padding:0;border-radius:50%;background:rgba(0,0,0,.55);font-size:13px;font-weight:400;line-height:18px}' +
'.tc-in .cmp-strip{position:absolute;left:0;right:0;bottom:100%;padding:8px 14px 6px;background:#fff;border-top:1px solid #EEF0F3}.tc-rto:not([hidden]) ~ .cmp-strip{bottom:calc(100% + 38px)}' +
'.tc-it{display:flex;gap:10px;padding:14px 0;border-bottom:1px solid #F2F3F5}.tc-it:last-child{border-bottom:0}.tc-av{flex:0 0 34px;width:34px;height:34px;border-radius:50%;object-fit:cover;background:#F2F3F5}' +
/* 11-03 用户评论先审核:自己的评论挂状态 */
'.tc-st{display:inline-block;margin-top:6px;padding:0 7px;border-radius:4px;font-size:11px;line-height:18px;font-weight:600;background:#F2F3F5;color:#86909C}.tc-st.pending{background:#FFF4E5;color:#E08A00}.tc-it.rej .tc-t{color:#A0A6AE}' +
@@ -57,7 +61,7 @@
'.tc-more{display:block;width:100%;margin-top:6px;height:40px;border:0;border-radius:99px;background:#F7F8FA;color:#4E5969;font-size:13px;font-weight:600}';
var SLUG = (location.pathname.match(/\/topic\/([^\/]+)\//) || [])[1] || '';
/* 11-04 微信转发图:每个专题一张 assets/share/t/<slug>.jpg(核心词大字 + logo),见 js/share-card.js */
if (SLUG) (function () { var s = document.createElement('script'); s.src = '../../js/share-card.js?v=26110458'; s.onload = function () { window.mbShare('../../assets/share/t/' + SLUG + '.jpg'); }; document.head.appendChild(s); })();
if (SLUG) (function () { var s = document.createElement('script'); s.src = '../../js/share-card.js?v=26110459'; s.onload = function () { window.mbShare('../../assets/share/t/' + SLUG + '.jpg'); }; document.head.appendChild(s); })();
var SK = 'mmbox.tp.v1', UK = (function () { var m = ((location.hash || '') + '&' + (location.search || '')).match(/[#?&]openid=([^&]*)/); return m ? decodeURIComponent(m[1]) : '_guest'; })();
function loadMe() { try { var a = JSON.parse(localStorage.getItem(SK) || '{}') || {}; var m = a[UK] || {}; m.cm = m.cm || {}; m.lk = m.lk || {}; return { a: a, m: m }; } catch (e) { return { a: {}, m: { cm: {}, lk: {} } }; } }
function saveMe(st) { try { st.a[UK] = st.m; localStorage.setItem(SK, JSON.stringify(st.a)); } catch (e) {} }
@@ -70,7 +74,7 @@
function getJSON(u) { return fetch(u, { cache: 'no-cache' }).then(function (r) { if (!r.ok) throw r.status; return r.json(); }); }
function related(box) {
getJSON('../_shared/idx.json?v=26110458').then(function (idx) {
getJSON('../_shared/idx.json?v=26110459').then(function (idx) {
var me = null; idx.forEach(function (x) { if (x[0] === SLUG) me = x; });
var tag = me ? me[2] : '', h = 0; for (var i = 0; i < SLUG.length; i++) h = (h * 31 + SLUG.charCodeAt(i)) >>> 0;
var rest = idx.filter(function (x) { return x[0] !== SLUG && (window.__mbUgc || ['ceshi', 'duiyou'].indexOf(x[0]) < 0); }); /* UGC 关着不推两个测试 */
@@ -92,19 +96,20 @@
var API = '/api/mp/public/mama-box/plaza';
function mine() {
var old = (st.m.cm[SLUG] || []).map(function (c) { return { t: c.t, ts: c.ts, s: c.s, st: 'local' }; });
return old.concat(RC.mine.slice().reverse().map(function (c) { return { t: c.text, ts: c.at, s: c.stage, st: c.status, why: c.reason, id: c.id, parent: c.parent, to: c.parentName }; }));
return old.concat(RC.mine.slice().reverse().map(function (c) { return { t: c.text, ts: c.at, s: c.stage, st: c.status, why: c.reason, id: c.id, parent: c.parent, to: c.parentName, img: c.images }; }));
}
function real() { return RC.list.filter(function (c) { return !c.parent; }).map(function (c) { return [c.nickname || '孕妈', c.avatar || '', c.stage || '', c.text, 0, [], 'r:' + c.id]; }); }
function real() { return RC.list.filter(function (c) { return !c.parent; }).map(function (c) { return [c.nickname || '孕妈', c.avatar || '', c.stage || '', c.text, 0, [], 'r:' + c.id, c.images]; }); }
var cpH = function (a) { return window.MBCmPics ? MBCmPics.html(a) : ''; }; /* 11-05 评论里的图 */
/* 11-03 用户:可以回复别人的评论。回复也先审核;楼里只有一层,回复回复也挂在同一楼,写「回复 XX」 */
var REPLY = null, MYN = '我';
function stTag(c) { return c.st === 'pending' ? '<span class="tc-st pending">审核中 · 通过后大家才能看到</span>' : c.st === 'rejected' ? '<span class="tc-st">未通过' + (c.why ? ':' + esc(c.why) : '') + '</span>' : c.st === 'local' ? '<span class="tc-st">仅自己可见</span>' : ''; }
function replies(base, k, owner) {
var L2 = (base || []).map(function (r) { return { n: r[0], t: r[3] }; })
.concat(RC.list.filter(function (c) { return c.parent === k; }).map(function (c) { return { n: c.nickname || '孕妈', t: c.text, to: c.parentName, ts: c.at }; })
.concat(mine().filter(function (c) { return c.parent === k; }).map(function (c) { return { n: MYN, me: 1, t: c.t, to: c.to, st: c.st, why: c.why, ts: c.ts }; }))
.concat(RC.list.filter(function (c) { return c.parent === k; }).map(function (c) { return { n: c.nickname || '孕妈', t: c.text, to: c.parentName, ts: c.at, img: c.images }; })
.concat(mine().filter(function (c) { return c.parent === k; }).map(function (c) { return { n: MYN, me: 1, t: c.t, to: c.to, st: c.st, why: c.why, ts: c.ts, img: c.img }; }))
.sort(function (x, y) { return x.ts - y.ts; })); /* 预置回复在前,真实回复按时间先后 */
return L2.length ? '<div class="tc-r">' + L2.map(function (r) {
return '<p' + (r.me ? (r.st === 'rejected' ? ' class="rej"' : '') : ' data-rk="' + esc(k) + '" data-rn="' + esc(r.n) + '"') + '><b>' + esc(r.n) + '</b>' + (r.to && r.to !== owner ? ' 回复 <b>' + esc(r.to) + '</b>' : '') + '<b>:</b>' + esc(r.t) +
return '<p' + (r.me ? (r.st === 'rejected' ? ' class="rej"' : '') : ' data-rk="' + esc(k) + '" data-rn="' + esc(r.n) + '"') + '><b>' + esc(r.n) + '</b>' + (r.to && r.to !== owner ? ' 回复 <b>' + esc(r.to) + '</b>' : '') + '<b>:</b>' + esc(r.t) + cpH(r.img) +
(r.me && r.st !== 'approved' ? '<br>' + stTag(r) : '') + '</p>';
}).join('') + '</div>' : '';
}
@@ -114,14 +119,14 @@
var tag = c.me ? stTag(c) : '';
return '<div class="tc-it' + (c.me && c.st === 'rejected' ? ' rej' : '') + '">' + (c.me ? '<span class="tc-av tc-me">我</span>' : '<img class="tc-av" src="' + esc(avP(c[1])) + '" alt="" loading="lazy">') +
'<div class="tc-b"' + (rk ? ' data-rk="' + esc(rk) + '" data-rn="' + esc(rn) + '"' : '') + '><div class="tc-n"><b>' + esc(c.me ? '我' : c[0]) + '</b>' + (c.me ? '<i>' + esc(c.s || myStage() || '刚刚') + '</i>' : (c[2] ? '<i>' + esc(c[2]) + '</i>' : '')) + '</div>' +
'<div class="tc-t">' + esc(c.me ? c.t : c[3]) + '</div>' + tag +
((c.me ? c.t : c[3]) ? '<div class="tc-t">' + esc(c.me ? c.t : c[3]) + '</div>' : '') + cpH(c.me ? c.img : c[7]) + tag +
(rk ? replies(c.me ? [] : c[5], rk, rn) : '') +
'<div class="tc-f">' + (rk ? '<button type="button" class="tc-rpy">回复</button>' : '') + '<button type="button" class="tc-lk' + (lk ? ' on' : '') + '" data-k="' + esc(key) + '">' + HEART + '<span>' + ((c.me ? 0 : c[4]) + (lk ? 1 : 0) || '赞') + '</span></button></div></div></div>';
}
function paint() {
var my = mine().filter(function (c) { return !c.parent; }).reverse(), A = real().concat(L), n = A.length + my.filter(function (c) { return c.st !== 'rejected'; }).length, show = open ? A : A.slice(0, 5);
box.innerHTML = '<div class="tt-h"><b>大家怎么说</b><span>' + n + ' 条评论</span></div>' +
'<div class="tc-list">' + my.map(function (c, i) { return item({ me: 1, t: c.t, s: c.s, st: c.st, why: c.why }, 'me' + c.ts); }).join('') +
'<div class="tc-list">' + my.map(function (c, i) { return item({ me: 1, t: c.t, s: c.s, st: c.st, why: c.why, img: c.img }, 'me' + c.ts); }).join('') +
show.map(function (c, i) { return item(c, c[6] || SLUG + '#' + (i - (A.length - L.length))); }).join('') + '</div>' +
(!open && A.length > 5 ? '<button type="button" class="tc-more">展开全部 ' + n + ' 条评论</button>' : '');
[].forEach.call(box.querySelectorAll('[data-rk]'), function (el) {
@@ -138,37 +143,50 @@
}
/* 底部浮条:整页常驻,发完滚到评论区看到自己那条 */
var bar = document.createElement('div'); bar.className = 'tc-in';
bar.innerHTML = '<div class="tc-rto" hidden><span></span><button type="button" aria-label="取消回复">×</button></div><label><textarea rows="1" maxlength="200" placeholder="说说你的情况,和其他妈妈聊聊…"></textarea></label><button type="button" disabled>发布</button>';
bar.innerHTML = '<div class="tc-rto" hidden><span></span><button type="button" aria-label="取消回复">×</button></div><div class="cmp-strip"></div><label><textarea rows="1" maxlength="200" placeholder="说说你的情况,和其他妈妈聊聊…"></textarea></label><button type="button" class="cmp-btn" aria-label="加图片"></button><button type="button" class="tc-go" disabled>发布</button>';
bar.style.setProperty('--tt-c', box.style.getPropertyValue('--tt-c'));
document.body.appendChild(bar);
var sc = box.parentNode; while (sc && sc !== document.body && !/(auto|scroll)/.test(getComputedStyle(sc).overflowY)) sc = sc.parentNode;
sc = sc || document.body; sc.style.paddingBottom = (parseFloat(getComputedStyle(sc).paddingBottom) || 0) + 64 + 'px';
var ta = bar.querySelector('textarea'), bt = bar.querySelector('label + button'), PH = ta.placeholder, rto = bar.querySelector('.tc-rto');
var ta = bar.querySelector('textarea'), bt = bar.querySelector('.tc-go'), PH = ta.placeholder, rto = bar.querySelector('.tc-rto');
function setReply(r, tx) {
REPLY = r; ta.placeholder = r ? '回复 ' + r.n + ':' : PH; rto.hidden = !r;
if (r) rto.querySelector('span').innerHTML = '回复 <b>' + esc(r.n) + '</b>' + (tx ? ':' + esc(String(tx).trim().slice(0, 40)) : '');
}
rto.querySelector('button').addEventListener('mousedown', function (e) { e.preventDefault(); });
rto.querySelector('button').addEventListener('click', function () { setReply(null); });
ta.addEventListener('blur', function () { setTimeout(function () { if (!ta.value.trim()) setReply(null); }, 150); });
ta.addEventListener('input', function () { bt.disabled = !ta.value.trim(); ta.style.height = '24px'; ta.style.height = Math.min(96, ta.scrollHeight) + 'px'; });
/* 11-05 评论 / 回复可带图(最多 3 张,js/cm-pics.js 加载好之后才出按钮);只有图没有字也能发 */
var CP = null, pb = bar.querySelector('.cmp-btn');
var canSend = function () { return !!ta.value.trim() || !!(CP && CP.count()); };
pb.hidden = true;
pb.addEventListener('mousedown', function (e) { e.preventDefault(); });
pb.addEventListener('click', function () { if (CP) CP.pick(); });
cmPicsReady(function () {
CP = MBCmPics.picker({ strip: bar.querySelector('.cmp-strip'), toast: toast, onChange: function () { bt.disabled = !canSend(); pb.classList.toggle('on', CP.count() > 0); } });
pb.innerHTML = MBCmPics.ICON; pb.hidden = false; paint();
});
ta.addEventListener('blur', function () { setTimeout(function () { if (!canSend()) setReply(null); }, 150); });
ta.addEventListener('input', function () { bt.disabled = !canSend(); ta.style.height = '24px'; ta.style.height = Math.min(96, ta.scrollHeight) + 'px'; });
bt.addEventListener('click', function () {
var v = ta.value.trim(); if (!v || bt.busy) return;
var v = ta.value.trim(); if (!canSend() || bt.busy) return;
if (CP && CP.busy()) { toast('图片还在上传,稍等一下'); return; }
var imgs = CP ? CP.urls() : [];
if (!v && !imgs.length) { toast('图片没传上去,删掉重新选一下'); return; }
if (!OID) { toast('请在小程序里打开后再评论'); return; }
/* 11-03:先交后台审核,通过后才公开;审核中只有自己看得到 */
bt.busy = true; bt.disabled = true;
var h1 = document.querySelector('h1');
fetch(API + '/comment?openid=' + encodeURIComponent(OID), { method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ kind: 'topic', target: SLUG, title: (h1 && h1.textContent.trim()) || document.title, text: v.slice(0, 200), stage: myStage(), parent: REPLY ? REPLY.k : '', parentName: REPLY ? REPLY.n : '' }) })
body: JSON.stringify({ kind: 'topic', target: SLUG, title: (h1 && h1.textContent.trim()) || document.title, text: v.slice(0, 200), images: imgs, stage: myStage(), parent: REPLY ? REPLY.k : '', parentName: REPLY ? REPLY.n : '' }) })
.then(function (r) { return r.json(); }).then(function (j) {
bt.busy = false;
if (!j || j.code !== 200) { bt.disabled = !ta.value.trim(); toast((j && j.message) || '没发出去,稍后再试'); return; }
if (!j || j.code !== 200) { bt.disabled = !canSend(); toast((j && j.message) || '没发出去,稍后再试'); return; }
RC.mine.unshift(j.result);
var wasReply = !!REPLY; setReply(null);
ta.value = ''; ta.style.height = '24px'; bt.disabled = true; ta.blur(); paint();
ta.value = ''; if (CP) CP.clear(); ta.style.height = '24px'; bt.disabled = true; ta.blur(); paint();
if (!wasReply) box.scrollIntoView({ block: 'start', behavior: 'smooth' });
toast(wasReply ? '回复已提交,审核通过后大家就能看到' : '已提交,审核通过后大家就能看到');
}).catch(function () { bt.busy = false; bt.disabled = !ta.value.trim(); toast('网络不太好,稍后再试'); });
}).catch(function () { bt.busy = false; bt.disabled = !canSend(); toast('网络不太好,稍后再试'); });
});
function toast(m) {
var t = document.createElement('div'); t.textContent = m;
@@ -177,13 +195,18 @@
}
fetch(API + '/comments?kind=topic&target=' + encodeURIComponent(SLUG) + (OID ? '&openid=' + encodeURIComponent(OID) : ''))
.then(function (r) { return r.json(); }).then(function (j) { if (j && j.code === 200) { RC = j.result; paint(); } }).catch(function () {});
getJSON('../_shared/cm/' + SLUG + '.json?v=26110458').then(function (d) { L = d.c || []; paint(); }).catch(function () { paint(); });
getJSON('../_shared/cm/' + SLUG + '.json?v=26110459').then(function (d) { L = d.c || []; paint(); }).catch(function () { paint(); });
}
function cmPicsReady(cb) {
if (window.MBCmPics) return cb();
var s = document.createElement('script'); s.src = '../../js/cm-pics.js?v=26110459'; s.onload = function () { if (window.MBCmPics) cb(); };
document.head.appendChild(s);
}
function ugcReady() {
if (window.__mbUgcReady) return window.__mbUgcReady;
return new Promise(function (res) {
var u = document.createElement('script'); u.src = '../../js/ugc.js?v=26110458';
var u = document.createElement('script'); u.src = '../../js/ugc.js?v=26110459';
u.onload = function () { (window.__mbUgcReady || Promise.resolve(false)).then(res); };
u.onerror = function () { res(false); };
document.head.appendChild(u);