孕期百问、孕期计算器加底部 Tab(同趣味小测试:本页/记一下/返回首页),抽成 js/mb-tabbar.js(v26110438)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
deploy
2026-11-05 00:52:11 +00:00
parent 0552450207
commit c4c9b9e9fd
26 changed files with 298 additions and 89 deletions

View File

@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26110437';
var VER = '26110438';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));

View File

@@ -244,5 +244,5 @@
}
/* feed.js 首页默认不加载(index.html 只引了 topics.js),这里按需拉一次 */
if (window.GC_FEED) init();
else { var fs = document.createElement('script'); fs.src = 'guangchang/feed.js?v=26110437'; fs.onload = fs.onerror = init; document.body.appendChild(fs); }
else { var fs = document.createElement('script'); fs.src = 'guangchang/feed.js?v=26110438'; fs.onload = fs.onerror = init; document.body.appendChild(fs); }
})();

View File

@@ -6,9 +6,9 @@
(function () {
var sec = document.getElementById('gcHome');
if (!sec) return;
var l = document.createElement('link'); l.rel = 'stylesheet'; l.href = 'js/gch/v4.css?v=26110437';
var l = document.createElement('link'); l.rel = 'stylesheet'; l.href = 'js/gch/v4.css?v=26110438';
document.head.appendChild(l);
var s = document.createElement('script'); s.src = 'js/gch/v4.js?v=26110437';
var s = document.createElement('script'); s.src = 'js/gch/v4.js?v=26110438';
s.onload = function () { if (sec.querySelector('.g4-foot')) merge(); else sec.addEventListener('gc4-ready', merge, { once: true }); }; /* v4 要先拉 feed.js 才渲染 */
document.body.appendChild(s);

View File

@@ -9,7 +9,7 @@
if (/[?&]kit=0(?:&|$)/.test(location.search)) return;
var box = document.getElementById('kit'), sec = document.getElementById('kitHome');
if (!box || !sec) return;
var V = '26110437';
var V = '26110438';
var l = document.createElement('link'); l.rel = 'stylesheet'; l.href = 'js/kit.css?v=' + V; document.head.appendChild(l);
document.documentElement.classList.add('has-kit');

193
js/mb-tabbar.js Normal file
View File

@@ -0,0 +1,193 @@
/* 底部胶囊 Tab(11-05 用户:「孕期百问、孕期计算器加上跟趣味小测试一样的底部 Tab」)
样式 / 行为照搬 guangchang/index.html 那条(左:本页 · 中:「记一下」橙色圆钮 · 右:返回首页 + 用户头像渐变圈),
类名统一加 mbt- 前缀,避免和首页自己的 .tabbar / .tab 撞车。
用法:MBTab.init({
root: '../../', // 到 mama-box 根目录的相对路径(取头像 / 回首页用)
first: { t: '孕期计算器', ic: 'calc' }, // 左边那格:本页名字 + 图标(calc / qa)
onFirst: fn, // 点左边(默认:滚回顶部)
onHome: fn, // 点「返回首页」(默认:跳 root 带身份)
onRec: fn, // 点「记一下」(默认:自带弹层,列后台已上架的孕期工具,同广场)
mount: el, scroller: el // 挂在哪(默认 body,position:fixed);滚回顶部用的容器
}) */
(function (g) {
'use strict';
var CSS = '' +
'.mbt-fade{position:fixed;left:0;right:0;bottom:0;z-index:29;pointer-events:none;height:calc(96px + env(safe-area-inset-bottom));background:linear-gradient(180deg,rgba(255,246,241,0),rgba(255,246,241,.75))}' +
'.mbt{position:fixed;left:16px;right:16px;bottom:calc(22px + env(safe-area-inset-bottom));height:62px;z-index:30;border-radius:31px;padding:0 6px;display:grid;grid-template-columns:repeat(3,1fr);font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif}' +
'.mbt::before{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;pointer-events:none;background:#FFF9F5;box-shadow:inset 0 0 0 1px rgba(255,255,255,.85),inset 0 1px 0 #fff;' +
'-webkit-mask:radial-gradient(circle 31px at 50% 6px,transparent 30.5px,#000 31.5px);mask:radial-gradient(circle 31px at 50% 6px,transparent 30.5px,#000 31.5px)}' +
'.mbt::after{content:"";position:absolute;inset:0;z-index:-2;border-radius:inherit;pointer-events:none;box-shadow:0 14px 34px rgba(200,100,70,.2),0 2px 6px rgba(200,100,70,.06)}' +
'.mbt-t{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;padding:0 0 6px;border:0;background:none;font-family:inherit;color:#A3958D;cursor:pointer;-webkit-tap-highlight-color:transparent}' +
'.mbt-t.on{color:#F4593C}' +
'.mbt-i{width:25px;height:25px;display:block;position:relative}' +
'.mbt-i svg{display:block;width:100%;height:100%;overflow:visible}' +
'.mbt-i .tf,.mbt-i .tl{fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}' +
'.mbt-t.on .tf{fill:currentColor}.mbt-t.on .tl{stroke:#fff}' +
'.mbt-x{font-size:12.5px;font-weight:600;line-height:1.1;letter-spacing:.2px;white-space:nowrap}.mbt-t.on .mbt-x{font-weight:800}' +
'.mbt-t::after{content:"";position:absolute;left:50%;bottom:6px;width:4px;height:4px;margin-left:-2px;border-radius:50%;background:#F4593C;opacity:0}.mbt-t.on::after{opacity:1}' +
'.mbt-fab::after{display:none}' +
'.mbt-fab .fab{position:absolute;left:50%;top:-20px;width:52px;height:52px;margin-left:-26px;border-radius:50%;display:grid;place-items:center;overflow:visible;' +
'background:linear-gradient(150deg,#FF8A6E,#F4593C);box-shadow:0 6px 14px rgba(244,89,60,.32),inset 0 1px 0 rgba(255,255,255,.3);transition:transform .3s cubic-bezier(.3,1.6,.5,1)}' +
'.mbt-fab .fab::before{content:"";display:block;width:20px;height:20px;opacity:.28;border-radius:2px;background:linear-gradient(#fff,#fff) center/100% 3px no-repeat,linear-gradient(#fff,#fff) center/3px 100% no-repeat}' +
'.mbt-fab:active .fab{transform:scale(.9)}' +
'.mbt-w{position:absolute;left:50%;top:50%;transform:translate(calc(-50% - 5px),-54%) rotate(-8deg);display:flex;align-items:center;white-space:nowrap;font-weight:900;font-style:normal;color:#4A1E14;line-height:1;pointer-events:none;' +
'text-shadow:1.6px 0 #fff,-1.6px 0 #fff,0 1.6px #fff,0 -1.6px #fff,1.2px 1.2px #fff,-1.2px 1.2px #fff,1.2px -1.2px #fff,-1.2px -1.2px #fff,0 3px 4px rgba(120,30,10,.25)}' +
'.mbt-w i{font-style:normal;font-size:31px}.mbt-w em{font-style:normal;font-size:12.5px;line-height:1;margin:9px 0 0;letter-spacing:-.5px}.mbt-w s{text-decoration:none;font-size:20px;display:inline-block;transform:skewX(-8deg)}' +
'.mbt-fab .mbt-x{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}' +
'.mbt-home .mbt-i{margin-bottom:6px}.mbt-home .mbt-x{font-size:11.5px;font-weight:600}' +
'.mbt-av{position:absolute;inset:-3px;width:calc(100% + 6px);height:calc(100% + 6px);border-radius:50%;object-fit:cover;display:none;background:#fff;box-shadow:0 0 0 1.5px #fff,0 3px 10px rgba(226,105,74,.3)}' +
'.mbt-hm{position:absolute;right:-8px;bottom:-5px;width:15px;height:15px;z-index:1;border-radius:50%;display:none;background:linear-gradient(150deg,#E2694A,#C24E31);box-shadow:0 0 0 1.5px #FFF6F1}.mbt-hm svg{display:block;width:10px;height:10px;margin:2.5px}' +
'.mbt-i.has-av .mbt-av,.mbt-i.has-av .mbt-hm{display:block}.mbt-i.has-av svg.ts{visibility:hidden}' +
'.mbt-i.has-av::before{content:"";position:absolute;inset:-7px;border-radius:50%;z-index:-1;pointer-events:none;background:conic-gradient(from 200deg,#FFB38A,#F4593C,#E2694A,#FF8FA3,#FFB38A);' +
'-webkit-mask:radial-gradient(circle closest-side,transparent 82%,#000 85%);mask:radial-gradient(circle closest-side,transparent 82%,#000 85%)}' +
/* 「记一下」弹层(同广场:标题一行 + 4 列工具宫格) */
'.mbt-mask{position:fixed;inset:0;z-index:60;background:rgba(40,20,14,.38);opacity:0;pointer-events:none;transition:opacity .25s}.mbt-mask.on{opacity:1;pointer-events:auto}' +
'.mbt-sh{position:fixed;left:0;right:0;bottom:0;z-index:61;max-height:80vh;overflow:auto;padding:8px 18px calc(20px + env(safe-area-inset-bottom));border-radius:22px 22px 0 0;background:#FFF9F5;transform:translateY(105%);transition:transform .3s cubic-bezier(.2,.9,.3,1);font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif}' +
'.mbt-sh.on{transform:none}' +
'.mbt-rh{display:flex;align-items:baseline;gap:10px;padding:10px 2px 14px}.mbt-rh b{font-size:19px;font-weight:800;color:#2A1E1E;flex:none}' +
'.mbt-rh span{font-size:12px;color:#9C8F88;flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}' +
'.mbt-rx{flex:none;align-self:center;width:30px;height:30px;border:0;border-radius:50%;display:grid;place-items:center;background:rgba(120,70,50,.08);color:#9C8F88;font-size:18px;line-height:1;cursor:pointer}' +
'.mbt-apps{display:grid;grid-template-columns:repeat(4,1fr);gap:20px 6px;padding:4px 0 8px}' +
'.mbt-app{position:relative;display:flex;flex-direction:column;align-items:center;gap:6px;min-width:0;border:0;background:none;font-family:inherit;color:#2A1E1E;cursor:pointer}' +
'.mbt-app img,.mbt-app .rl{width:48px;height:48px;display:block;object-fit:contain;border-radius:14px}' +
'.mbt-app .rl{display:grid;place-items:center;background:#F6D9CB;color:#C2553A;font-size:19px;font-weight:800}' +
'.mbt-app span{display:block;max-width:100%;font-size:11.5px;font-weight:600;letter-spacing:-.2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}' +
'.mbt-app.off img,.mbt-app.off .rl{opacity:.45;filter:grayscale(.4)}' +
'.mbt-app.off i{position:absolute;top:-8px;left:calc(50% + 4px);font-style:normal;font-size:9px;font-weight:700;line-height:1;padding:3px 5px;border-radius:99px;background:#B9A9A2;color:#fff;white-space:nowrap}' +
'.mbt-empty{grid-column:1/-1;padding:30px 0;text-align:center;font-size:13px;color:#9C8F88}' +
'.mbt-toast{position:fixed;left:50%;bottom:calc(110px + env(safe-area-inset-bottom));z-index:70;max-width:80vw;padding:9px 14px;border-radius:12px;background:rgba(40,28,24,.86);color:#fff;font-size:13px;transform:translate(-50%,8px);opacity:0;transition:.25s;pointer-events:none}' +
'.mbt-toast.on{opacity:1;transform:translate(-50%,0)}';
var IC = {
calc: '<svg viewBox="0 0 24 24" aria-hidden="true"><rect class="tf" x="5" y="3.5" width="14" height="17" rx="2.8"/><path class="tl" d="M8.6 7.6h6.8M8.8 12h.01M12 12h.01M15.2 12h.01M8.8 15.6h.01M12 15.6h.01M15.2 15.6h.01" stroke-width="2.2"/></svg>',
qa: '<svg viewBox="0 0 24 24" aria-hidden="true"><path class="tf" d="M4 6.6A2.6 2.6 0 0 1 6.6 4h10.8A2.6 2.6 0 0 1 20 6.6v7.8a2.6 2.6 0 0 1-2.6 2.6H11l-4.2 3.2c-.4.3-.9 0-.9-.4V17A2.6 2.6 0 0 1 4 14.4z"/><path class="tl" d="M10.2 8.9a1.9 1.9 0 1 1 2.6 1.8c-.5.2-.8.6-.8 1.1v.4M12 14.2h.01"/></svg>'
};
var HOME = '<svg class="ts" viewBox="0 0 24 24" aria-hidden="true"><path class="tf" d="M4 10.4 12 4l8 6.4V18a2.4 2.4 0 0 1-2.4 2.4H6.4A2.4 2.4 0 0 1 4 18z"/><path class="tl" d="M9.8 20.2v-4.4a2.2 2.2 0 0 1 4.4 0v4.4"/></svg>';
function param(n) {
var m = location.search.match(new RegExp('[?&]' + n + '=([^&]*)')) || location.hash.match(new RegExp('[#&]' + n + '=([^&]*)'));
try { return m ? decodeURIComponent(m[1]) : ''; } catch (e) { return ''; }
}
function haptic() { try { navigator.vibrate && navigator.vibrate(8); } catch (e) {} }
function esc(s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]; }); }
var realAv = function (u) { return !!u && !/\/uploads\/system\//.test(u); };
function defAv(oid) { var h = 0, s = String(oid || ''); for (var i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0; var n = (h % 20) + 1; return 'https://mp.yizhezhe.com/_bridge/avatars/mom-' + (n < 10 ? '0' : '') + n + '.png'; }
function init(o) {
o = o || {};
var root = o.root || '../../', mount = o.mount || document.body;
if (!document.getElementById('mbtCss')) { var st = document.createElement('style'); st.id = 'mbtCss'; st.textContent = CSS; document.head.appendChild(st); }
var fade = document.createElement('div'); fade.className = 'mbt-fade'; fade.setAttribute('aria-hidden', 'true');
var bar = document.createElement('nav'); bar.className = 'mbt';
var f = o.first || { t: '本页', ic: 'qa' };
bar.innerHTML = '<button type="button" class="mbt-t on" data-k="first"><span class="mbt-i">' + (IC[f.ic] || IC.qa) + '</span><span class="mbt-x">' + esc(f.t) + '</span></button>' +
'<button type="button" class="mbt-t mbt-fab" data-k="rec"><span class="fab"><b class="mbt-w" aria-hidden="true"><i>记</i><em>一下</em><s>!</s></b></span><span class="mbt-x">记一下</span></button>' +
'<button type="button" class="mbt-t mbt-home" data-k="home"><span class="mbt-i">' + HOME + '</span><span class="mbt-x">返回首页</span></button>';
mount.appendChild(fade); mount.appendChild(bar);
var OPENID = param('openid');
function homeURL() {
var q = [], oid = param('openid'), uid = param('unionid');
if (oid) q.push('openid=' + encodeURIComponent(oid));
if (uid) q.push('unionid=' + encodeURIComponent(uid));
return root + (q.length ? '#' + q.join('&') : location.hash);
}
function goApp(url) {
try { var u = new URL(url, location.href); if (u.origin === location.origin || /(^|\.)yizhezhe\.com$/.test(u.hostname)) url = u.pathname + u.search; } catch (e) {}
var q = [], oid = param('openid'), uid = param('unionid');
if (oid) q.push('openid=' + encodeURIComponent(oid));
if (uid) q.push('unionid=' + encodeURIComponent(uid));
if (!oid && !uid) q.push('__no_identity=1');
var target = url + (url.indexOf('?') >= 0 ? '&' : '?') + q.join('&');
try {
if (oid) {
var api = '/api/mp/public/last-app?openid=' + encodeURIComponent(oid) + '&url=' + encodeURIComponent(location.origin + url);
if (!(navigator.sendBeacon && navigator.sendBeacon(api)) && window.fetch) fetch(api, { method: 'POST', keepalive: true }).catch(function () {});
}
if (window.wxAuth && wxAuth.reportLocation) wxAuth.reportLocation({ href: target });
} catch (e) {}
location.href = target;
}
var tt;
function toast(s) {
var t = document.querySelector('.mbt-toast');
if (!t) { t = document.createElement('div'); t.className = 'mbt-toast'; document.body.appendChild(t); }
t.textContent = s; t.classList.add('on'); clearTimeout(tt); tt = setTimeout(function () { t.classList.remove('on'); }, 2200);
}
/* 「记一下」:后台 App 表已上架、category=孕期工具(同首页 / 广场);数胎动 16 / 数宫缩 20 / 胎儿估重 15 周前灰掉 */
var GATE = { taidong: [16, '数胎动'], gongsuopinlv: [20, '数宫缩'], taierguzhong: [15, '胎儿估重'] }, apps = null, busy = false, mask, sh;
var myWeek = (function () { try { var w = JSON.parse(localStorage.getItem('mmbox.gcwk.v1') || 'null'); return w && !w.pp && w.w ? +w.w : 0; } catch (e) { return 0; } })();
function locked(a) { var x = GATE[a.identifier]; return x && myWeek && myWeek < x[0] ? x : null; }
function appsHTML() {
if (!apps) return '<div class="mbt-empty">加载中…</div>';
if (!apps.length) return '<div class="mbt-empty">暂时没取到工具,稍后再试</div>';
return apps.filter(function (a) { return !locked(a); }).concat(apps.filter(locked)).map(function (a) {
var x = locked(a), nm = a.name || a.identifier || '工具', ic = a.logo || a.icon;
return '<button type="button" class="mbt-app' + (x ? ' off' : '') + '" data-rid="' + esc(a.identifier || '') + '">' +
(ic ? '<img src="' + esc(ic) + '" alt="">' : '<b class="rl">' + esc(nm.slice(0, 1)) + '</b>') + '<span>' + esc(nm) + '</span>' + (x ? '<i>孕' + x[0] + '周开放</i>' : '') + '</button>';
}).join('');
}
function load() {
if (apps || busy) return; busy = true;
fetch('/api/mp/apps?isPublished=1&pageSize=200').then(function (r) { return r.ok ? r.json() : null; }).then(function (d) {
apps = ((d && d.result && d.result.items) || []).filter(function (a) { return a.url && a.category === '孕期工具'; })
.sort(function (x, y) { return (y.seq || 0) - (x.seq || 0) || (y.no || 0) - (x.no || 0); });
}).catch(function () { apps = []; }).then(function () { busy = false; var b = sh && sh.querySelector('.mbt-apps'); if (b) b.innerHTML = appsHTML(); });
}
function closeRec() { if (sh) { sh.classList.remove('on'); mask.classList.remove('on'); } }
function openRec() {
load();
if (!sh) {
mask = document.createElement('div'); mask.className = 'mbt-mask';
sh = document.createElement('div'); sh.className = 'mbt-sh'; sh.setAttribute('role', 'dialog');
document.body.appendChild(mask); document.body.appendChild(sh);
mask.addEventListener('click', closeRec);
sh.addEventListener('click', function (e) {
if (e.target.closest('.mbt-rx')) { closeRec(); return; }
var b = e.target.closest('[data-rid]'); if (!b || !apps) return;
var a = apps.filter(function (x) { return (x.identifier || '') === b.dataset.rid; })[0]; if (!a) return;
var x = locked(a);
if (x) { haptic(); toast(x[1] + '孕 ' + x[0] + ' 周以后开放,你现在孕 ' + myWeek + ' 周,还用不到哦'); return; }
haptic(); goApp(a.url);
});
}
sh.innerHTML = '<div class="mbt-rh"><b>记一下</b><span>兜爸心选的孕期工具</span><button type="button" class="mbt-rx" aria-label="关闭">×</button></div><div class="mbt-apps">' + appsHTML() + '</div>';
requestAnimationFrame(function () { mask.classList.add('on'); sh.classList.add('on'); });
}
bar.addEventListener('click', function (e) {
var b = e.target.closest('.mbt-t'); if (!b) return;
haptic();
var k = b.dataset.k;
if (k === 'first') { if (o.onFirst) o.onFirst(); else { var s = o.scroller || document.getElementById('scroll') || document.scrollingElement; s && s.scrollTo({ top: 0, behavior: 'smooth' }); } }
else if (k === 'rec') { if (o.onRec) o.onRec(); else openRec(); }
else if (o.onHome) o.onHome(); else location.href = homeURL();
});
/* 返回首页的头像(同广场):user-center/info 真头像 → hash 里的 avatar → 按 openid 固定默认头像;加载失败露回小房子 */
var ico = bar.querySelector('.mbt-home .mbt-i');
function paintAv(url) {
if (!url) return;
var im = ico.querySelector('.mbt-av');
if (!im) {
im = document.createElement('img'); im.className = 'mbt-av'; im.alt = ''; ico.appendChild(im);
ico.insertAdjacentHTML('beforeend', '<i class="mbt-hm" aria-hidden="true"><svg viewBox="0 0 24 24" fill="#fff"><path d="M12 4 3.5 11H6v8.5h4.5v-5h3v5H18V11h2.5z"/></svg></i>');
}
im.onload = function () { ico.classList.add('has-av'); };
im.onerror = function () { if (im.getAttribute('src') !== defAv(OPENID)) im.src = defAv(OPENID); else ico.classList.remove('has-av'); };
if (im.getAttribute('src') !== url) im.src = url;
}
function loadAv() {
if (!OPENID || !window.fetch) return;
fetch('/api/mp/public/user-center/info?openid=' + encodeURIComponent(OPENID) + (param('unionid') ? '&unionid=' + encodeURIComponent(param('unionid')) : ''))
.then(function (r) { return r.ok ? r.json() : null; }).then(function (d) {
var av = d && d.result && d.result.found && d.result.data && d.result.data.avatar;
if (realAv(av)) paintAv(av);
}).catch(function () {});
}
if (OPENID) { paintAv(realAv(param('avatar')) ? param('avatar') : defAv(OPENID)); loadAv(); }
window.addEventListener('pageshow', function (e) { if (e.persisted) loadAv(); });
return { bar: bar, fade: fade, openRec: openRec, closeRec: closeRec };
}
g.MBTab = { init: init };
})(window);

View File

@@ -8,7 +8,7 @@
postMessage 不是实时投递,微信在分享那一刻才派发,所以每次换内容都要再报一次;子页面没引 jweixin,这里按需加载。 */
(function (g) {
'use strict';
var V = '26110437';
var V = '26110438';
var isWx = /MicroMessenger/i.test(navigator.userAgent || '');
var waiting = [], loading = false;
function ready(cb) {