Files
mama-box/topic/_shared/tools.js

153 lines
15 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.
/* 孕期专题页共用:推荐工具卡(2026-10-31)
* 工具清单与 /api/mp/apps?isPublished=1 的「孕期工具」一致;跳转一律同源相对路径,带上 search + hash(openid)。
* 用法:TopicTools.render(el, [{id:'taidong', why:'一句推荐理由'}...], {title:'用得上的工具', color:'#18A058', wk:当前孕周})
*/
(function () {
var T = {
taidong: { n: '数胎动', u: '/taidong/', l: '/taidong/logo/logo-foot.svg', gate: 16 },
gongsuopinlv: { n: '数宫缩', u: '/gongsuo-pinlv/', l: '/upload/picture/1c02d720e4d23b09c996e859703b8b5e.svg', gate: 20 },
taierguzhong: { n: '胎儿估重', u: '/taier-guzhong/', l: '/upload/picture/715333a69fb67d8812ab093f342fa00f.png', gate: 15 },
chanjianrili: { n: '孕检日历', u: '/chanjian-rili/', l: '/chanjian-rili/logo/logo-7-dots.svg' },
yunqitizhong: { n: '孕期体重管理', u: '/yunqi-tizhong/', l: '/upload/picture/a6d7e61edee2ff0395cf00587a7c4036.svg' },
yunqixuetang: { n: '孕期血糖记录', u: '/yunqi-xuetang/', l: '/upload/picture/fde3c920fae453f2642731e4135b88f5.png' },
yunqinengbuchi: { n: '孕期能不能吃', u: '/yunqi-nengbuchi/', l: '/upload/picture/cf8750e19250caacd703b9d074211488.png' },
daichanqingdan: { n: '待产清单', u: '/daichan-qingdan/', l: '/upload/picture/f2da62ce728d3a7e85b4440ece6f26e5.svg' },
bushui: { n: '孕期补水补剂', u: '/yunqi-bushui/yindao/', l: '/upload/picture/ca30770e30bc2270c4aea0d382b872b0.svg' },
yun: { n: '孕期血压记录', u: '/yunqi-xueya/intro/', l: '/upload/picture/816227e3933211145bb8fc774ee32e82.svg' },
yundujinian: { n: '宝宝相册', u: '/yundu-jinian/', l: '/yundu-jinian/logo.svg' }
};
var css = '.tt-wrap{margin:28px 0 8px}.tt-h{display:flex;align-items:baseline;justify-content:space-between;margin:0 2px 12px}' +
'.tt-h b{font-size:18px;font-weight:800;color:#1F2329}.tt-h span{font-size:12.5px;color:#86909C}' +
'.tt-list{display:flex;flex-direction:column;gap:10px}' +
'.tt-it{display:flex;align-items:center;gap:12px;padding:12px 14px;background:#fff;border:1px solid #EEF0F3;border-radius:18px;text-decoration:none;-webkit-tap-highlight-color:transparent;transition:transform .15s}' +
'.tt-it:active{transform:scale(.98)}.tt-ic{flex:0 0 44px;width:44px;height:44px;border-radius:13px;background:var(--tt-bg,#F7F8FA);display:flex;align-items:center;justify-content:center}' +
'.tt-ic img{width:34px;height:34px;object-fit:contain}.tt-tx{flex:1;min-width:0}.tt-n{font-size:15px;font-weight:700;color:#1F2329;display:flex;align-items:center;gap:6px}' +
'.tt-tag{font-size:11px;font-weight:600;padding:1px 7px;border-radius:99px;background:#F2F3F5;color:#86909C}' +
'.tt-w{font-size:12.5px;color:#4E5969;margin-top:2px;line-height:1.5}.tt-go{flex:0 0 auto;font-size:13px;font-weight:700;color:var(--tt-c,#18A058);padding:6px 12px;border-radius:99px;background:var(--tt-bg,#F7F8FA)}';
function href(u) { return u + (location.search || '') + (location.hash || ''); }
function esc(s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); }
/* 11-02 用户:推荐工具做成固定模块,下面再推荐 3 个相关专题,再下面是评论区(预置评论 + 用户可评)。
* 104 个专题页都走 render(),所以统一在这里追加,页面不用改。
* 预置评论是 AI 写的模拟评论(AGENTS.md 12c),数据 cm/<slug>.json;用户自己发的只存本机 mmbox.tp.v1。 */
var css2 = '.tt-wrap{padding:16px 14px 14px;border-radius:22px;background:var(--tt-bg,#F7F8FA);border:0}.tt-wrap .tt-it{border-color:transparent;box-shadow:0 4px 14px -10px rgba(31,35,41,.25)}' +
'.tt-h b{display:flex;align-items:center;gap:6px}.tt-h b::before{content:"";width:4px;height:16px;border-radius:4px;background:var(--tt-c,#18A058)}' +
'.tx-sec{margin:26px 0 8px}.tx-sec>.tt-h{margin-bottom:12px}' +
/* 11-03 用户:相关推荐不要图、别占太大 → 紧凑文字行:标题 + 分类 · N 人看过 */
'.tr-list{border-radius:16px;background:#fff;border:1px solid #EEF0F3;padding:0 12px}.tr-it{display:flex;align-items:center;gap:10px;padding:11px 0;border-bottom:1px solid #F2F3F5;text-decoration:none;-webkit-tap-highlight-color:transparent}.tr-it:last-child{border-bottom:0}' +
'.tr-it:active{opacity:.6}' +
'.tr-tx{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}.tr-tx b{font-size:14.5px;font-weight:600;line-height:1.45;color:#1F2329;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}' +
'.tr-tx span{font-size:12px;color:#86909C;display:flex;gap:6px;align-items:center}.tr-tx em{font-style:normal;font-size:11px;font-weight:600;color:var(--tt-c,#18A058)}.tr-tx em::after{content:"·";margin-left:6px;color:#C9CDD4;font-weight:400}.tr-go{color:#C9CDD4;font-size:18px}' +
/* 11-03 用户:评论输入框别放在评论区最上面,浮在页面底部 */
'.tc-in{position:fixed;left:0;right:0;bottom:0;z-index:30;display:flex;gap:8px;align-items:flex-end;padding:8px 12px calc(8px + env(safe-area-inset-bottom));background:rgba(255,255,255,.97);border-top:1px solid #F0F1F3;box-shadow:0 -6px 18px -14px rgba(31,35,41,.3)}' +
'.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}' +
'.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}' +
'.tc-me{display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#fff;background:var(--tt-c,#18A058)}' +
'.tc-b{flex:1;min-width:0}.tc-n{display:flex;align-items:center;gap:6px;font-size:13px;color:#86909C;line-height:1.4}.tc-n b{font-weight:600;color:#4E5969}.tc-n i{font-style:normal;font-size:11px;padding:0 6px;border-radius:99px;background:#F2F3F5}' +
'.tc-t{margin-top:4px;font-size:14.5px;line-height:1.7;color:#1F2329;word-break:break-word}' +
'.tc-f{display:flex;justify-content:flex-end;margin-top:2px}.tc-lk{display:inline-flex;align-items:center;gap:3px;border:0;background:none;padding:2px 0;font-size:12px;color:#86909C}.tc-lk svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.8}' +
'.tc-lk.on{color:#F2533A}.tc-lk.on svg{fill:currentColor}' +
'.tc-r{margin-top:6px;padding:8px 10px;border-radius:12px;background:#F7F8FA;font-size:13.5px;line-height:1.65;color:#4E5969}.tc-r p+p{margin-top:4px}.tc-r b{font-weight:600;color:#1F2329}' +
'.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] || '';
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) {} }
/* 11-02 用户:自己发的评论带上孕周(首页写进 mmbox.gcwk.v1) */
function myStage() { try { var g = JSON.parse(localStorage.getItem('mmbox.gcwk.v1') || 'null'); return !g ? '' : g.pp ? '宝妈' : g.w ? '孕 ' + g.w + ' 周' : ''; } catch (e) { return ''; } }
function avP(a) { return typeof a === 'number' ? '../../guangchang/av/' + ('000' + a).slice(-4) + '.webp' : a.indexOf('av:') === 0 ? '../../guangchang/av/' + a.slice(3) + '.webp' : a.charAt(0) === '/' ? a : '../../assets/img/mom/' + a + '.jpg'; }
function wan(n) { return n >= 1e4 ? (Math.round(n / 1e3) / 10) + '万' : String(n); }
var HEART = '<svg viewBox="0 0 24 24"><path d="M12 20s-7-4.4-7-10a4 4 0 017-2.6A4 4 0 0119 10c0 5.6-7 10-7 10z"/></svg>';
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=26110239').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; });
var same = rest.filter(function (x) { return x[2] === tag; }).sort(function (a, b) { return b[3] - a[3]; });
/* 同类按热度取前几里轮换,凑不够 3 个用全站热门补 */
var pick = [], top = same.slice(0, 6);
for (var k = 0; k < top.length && pick.length < 3; k++) pick.push(top[(h + k) % top.length]);
rest.sort(function (a, b) { return b[3] - a[3]; }).forEach(function (x) { if (pick.length < 3 && pick.indexOf(x) < 0) pick.push(x); });
box.innerHTML = '<div class="tt-h"><b>相关推荐</b><span>其他妈妈也在看</span></div><div class="tr-list">' + pick.map(function (x) {
return '<a class="tr-it" href="' + esc(href('../' + x[0] + '/')) + '"><span class="tr-tx"><b>' + esc(x[1]) + '</b><span>' + (x[2] ? '<em>' + esc(x[2]) + '</em>' : '') + wan(Math.round(1510000 * x[3])) + ' 人看过</span></span><span class="tr-go">›</span></a>';
}).join('') + '</div>';
}).catch(function () { box.remove(); });
}
function comments(box) {
var st = loadMe(), L = [], open = false;
function mine() { return st.m.cm[SLUG] || []; }
function item(c, key) {
var lk = !!st.m.lk[key];
return '<div class="tc-it">' + (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"><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>' +
(!c.me && c[5] && c[5].length ? '<div class="tc-r">' + c[5].map(function (r) { return '<p><b>' + esc(r[0]) + ':</b>' + esc(r[3]) + '</p>'; }).join('') + '</div>' : '') +
'<div class="tc-f"><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().slice().reverse(), n = L.length + my.length, show = open ? L : L.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 }, 'me' + c.ts); }).join('') +
show.map(function (c, i) { return item(c, SLUG + '#' + i); }).join('') + '</div>' +
(!open && L.length > 5 ? '<button type="button" class="tc-more">展开全部 ' + n + ' 条评论</button>' : '');
var mo = box.querySelector('.tc-more'); if (mo) mo.addEventListener('click', function () { open = true; paint(); });
[].forEach.call(box.querySelectorAll('.tc-lk'), function (b) {
b.addEventListener('click', function () { var k = b.dataset.k; if (st.m.lk[k]) delete st.m.lk[k]; else st.m.lk[k] = 1; saveMe(st); paint(); });
});
}
/* 底部浮条:整页常驻,发完滚到评论区看到自己那条 */
var bar = document.createElement('div'); bar.className = 'tc-in';
bar.innerHTML = '<label><textarea rows="1" maxlength="200" placeholder="说说你的情况,和其他妈妈聊聊…"></textarea></label><button type="button" 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('button');
ta.addEventListener('input', function () { bt.disabled = !ta.value.trim(); ta.style.height = '24px'; ta.style.height = Math.min(96, ta.scrollHeight) + 'px'; });
bt.addEventListener('click', function () {
var v = ta.value.trim(); if (!v) return;
(st.m.cm[SLUG] = mine()).push({ t: v.slice(0, 200), ts: Date.now(), s: myStage() }); saveMe(st);
ta.value = ''; ta.style.height = '24px'; bt.disabled = true; ta.blur(); paint();
box.scrollIntoView({ block: 'start', behavior: 'smooth' });
});
getJSON('../_shared/cm/' + SLUG + '.json?v=26110239').then(function (d) { L = d.c || []; paint(); }).catch(function () { paint(); });
}
function extras(el) {
if (!SLUG || document.getElementById('tx-rel')) return;
if (!document.getElementById('tx-css')) { var s = document.createElement('style'); s.id = 'tx-css'; s.textContent = css2; document.head.appendChild(s); }
var a = document.createElement('section'); a.id = 'tx-rel'; a.className = 'tx-sec';
var b = document.createElement('section'); b.id = 'tx-cm'; b.className = 'tx-sec';
el.parentNode.insertBefore(b, el.nextSibling); el.parentNode.insertBefore(a, b);
var c = getComputedStyle(el);
[a, b].forEach(function (x) { x.style.marginLeft = c.marginLeft; x.style.marginRight = c.marginRight; });
a.style.setProperty('--tt-c', c.getPropertyValue('--tt-c')); a.style.setProperty('--tt-bg', c.getPropertyValue('--tt-bg'));
b.style.setProperty('--tt-c', c.getPropertyValue('--tt-c')); b.style.setProperty('--tt-bg', c.getPropertyValue('--tt-bg'));
related(a); comments(b);
}
function render(el, list, opt) {
if (!el) return;
opt = opt || {};
if (!document.getElementById('tt-css')) { var s = document.createElement('style'); s.id = 'tt-css'; s.textContent = css; document.head.appendChild(s); }
var wk = +opt.wk || 0, html = '';
list.forEach(function (x) {
var t = T[x.id]; if (!t) return;
var lock = t.gate && wk && wk < t.gate;
html += '<a class="tt-it" href="' + esc(href(t.u)) + '"><span class="tt-ic"><img src="' + esc(t.l) + '" alt=""></span>' +
'<span class="tt-tx"><span class="tt-n">' + esc(t.n) + (lock ? '<span class="tt-tag">孕' + t.gate + '周开放</span>' : (x.tag ? '<span class="tt-tag">' + esc(x.tag) + '</span>' : '')) + '</span>' +
'<span class="tt-w">' + esc(x.why) + '</span></span><span class="tt-go">' + esc(x.btn || '去用') + '</span></a>';
});
el.classList.add('tt-wrap');
if (opt.color) el.style.setProperty('--tt-c', opt.color);
if (opt.bg) el.style.setProperty('--tt-bg', opt.bg);
el.innerHTML = '<div class="tt-h"><b>' + esc(opt.title || '用得上的工具') + '</b><span>' + esc(opt.sub || '都在兜爸心选,免费用') + '</span></div><div class="tt-list">' + html + '</div>';
extras(el);
}
window.TopicTools = { render: render, list: T, href: href };
})();