Files
mama-box/js/kit.js

179 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.
/* 11-03 孕期百宝箱(用户当天四轮改):
① 「把此刻那个板块改成测一测、科普、计算器」→ ② 「进去之后是列表页,名字改得大家看得懂」→ 三个入口:
趣味小测试 → 孕妈广场测试列表(guangchang/?c=ceshi);孕期百问 → 首页全部问题页(app.js openQA);孕期计算器 → topic/jisuan/
③ 「加上多少万人测过、算过」→ 卡上人数行(模拟数,AGENTS.md 12b,12-31 复核)
④ 「百宝箱单独拎出来做一个模块,下面放一个像『其他妈妈也在搜』的列表,三块各拿两个」→ 独立 section #kitHome(孕妈广场上面),
入口下面 6 条:测试 / 百问 / 计算器 交替各 2 条,按孕周挑,点了直达那一项。
原「此刻」#pulse 只是藏起来(kit.css),JS 照跑(别处要用它的总人数);地址带 ?kit=0 可看回旧版。 */
(function () {
if (/[?&]kit=0(?:&|$)/.test(location.search)) return;
var box = document.getElementById('kit'), sec = document.getElementById('kitHome');
if (!box || !sec) return;
var V = '26110282';
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');
function esc(t) { return String(t == null ? '' : t).replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); }
function cleanQ(extra) {
var q = new URLSearchParams(location.search);
['t', 'n', 'c', 'id', 'mine', 'write', 'fav', 'kit'].forEach(function (k) { q.delete(k); });
if (extra) Object.keys(extra).forEach(function (k) { q.set(k, extra[k]); });
var s = q.toString(); return (s ? '?' + s : '') + location.hash;
}
function wan(n) { return n >= 1e6 ? Math.round(n / 1e4) + '万' : n >= 1e4 ? (Math.round(n / 1e3) / 10) + '万' : String(Math.round(n)); } /* 百万以上取整,卡片窄放得下 */
/* 入口专用:万为单位、必带一位小数,碰上整数(如 68.0)挪 0.3,避免看着像凑的整数 */
function wanX(n) { var v = Math.round(n / 1e3) / 10; if (v % 1 === 0) v = +(v + .3).toFixed(1); return v.toFixed(1) + '万'; }
function total() { var p = window.__mbPulse; return (p && p.total && +p.total.value) || 1510000; }
var FIRE = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12.6 2.5c.4 3-1.6 4.6-3 6.2C8 10.4 7 12 7 14.3A5 5 0 0012 19.5a5 5 0 005-5.2c0-2-1-3.6-1.9-4.6-.2 1.3-.9 2.2-1.9 2.6.6-3.4-.2-7-.6-9.8z" fill="currentColor"/></svg>';
/* ---------- 上:三个入口 ---------- */
var E = [
{ k: 'qz', n: '趣味小测试', d: '1 分钟测出你是哪种孕妈', c: '30 个测试', img: 'ceshi', u: '人测过' },
{ k: 'qa', n: '孕期百问', d: '孕妈最常问的问题,一次看明白', c: '100 个问题', img: 'kepu', u: '人看过' },
{ k: 'js', n: '孕期计算器', d: '花多少钱、休几天产假,算一算', c: '4 个计算器', img: 'jisuan', u: '人算过' }
];
box.innerHTML = '<div class="kit-hd"><h2 class="kit-tt"><span>孕期百宝箱</span></h2><span>测一测 · 查一查 · 算一算</span></div><div class="kit-pn"><div class="kit-g">' +
E.map(function (x) {
return '<button type="button" class="kit-e e-' + x.k + '" data-k="' + x.k + '"><img src="assets/img/kit/' + x.img + '.webp" alt="" decoding="async">' +
'<b>' + x.n + '</b><span>' + x.d + '</span><strong class="kit-n">' + FIRE + '<i>--</i>' + x.u + '</strong><em>' + x.c + '<i aria-hidden="true">›</i></em></button>';
}).join('') + '</div>' +
/* 11-03 用户:「『大家都在用』跟百宝箱像两块,割裂了」→ 去掉这个小标题和虚线,三个入口 + 6 条装进同一张白底面板(.kit-pn),一眼看出是百宝箱里的内容 */
'<div class="ms-list kit-list"></div></div>';
sec.classList.remove('hide');
function setCnt(k, t) { var e = box.querySelector('.e-' + k + ' em'); if (e) e.firstChild.nodeValue = t; }
function setNum(k, n) { var e = box.querySelector('.e-' + k + ' .kit-n i'); if (e && n) e.textContent = wanX(n); }
/* 人数口径(11-03 改):测过 = 30 个测试 gcTested 之和 × .456;看过 = 「此刻」总人数 × .574;算过 = 4 个计算器 heat 之和 × 总人数(同 topic/jisuan) */
function nums() {
/* 11-03 用户:「三个入口的人数太大,几十万就行,要有零有整」→ 系数压到几十万(测过 ≈68 万 / 看过 ≈87 万 / 算过 ≈50 万),且一律带一位小数(wanX)。
仍不小于下面列表里单条的最大值(单个测试 ≤12 万、单条百问 ≤64 万、单个计算器 ≤18 万),口径不打架。模拟数,12-31 复核 */
if (window.GC_TESTS_META && window.gcTested) { setNum('qz', window.GC_TESTS_META.reduce(function (a, x) { return a + window.gcTested(x.id); }, 0) * .456); setCnt('qz', window.GC_TESTS_META.length + ' 个测试'); }
setNum('qa', total() * .574);
setNum('js', total() * CALC.reduce(function (a, x) { return a + x.heat; }, 0));
if (QA && QA.length) setCnt('qa', QA.length + ' 个问题');
}
/* ---------- 下:6 条列表(测试 / 百问 / 计算器 交替,各 2 条) ---------- */
/* 测试按孕周段分池(段同 app.js qaBand:1 ≤8 / 2 ≤13 / 3 ≤20 / 4 ≤28 / 5 ≤36 / 6 37+;0 = 没孕周 / 产后),每天轮换起点 */
var QZ_POOL = {
0: ['t11', 't14', 't13', 't16', 't18', 't23', 't03', 't22'],
1: ['t17', 't01', 't25', 't24', 't02', 't11'],
2: ['t17', 't01', 't25', 't24', 't02', 't11'],
3: ['t24', 't25', 't01', 't17', 't21', 't02'],
4: ['t24', 't21', 't25', 't01', 't02', 't17'],
5: ['t05', 't21', 't02', 't24', 't03', 't25'],
6: ['t05', 't03', 't02', 't06', 't21', 't25']
};
/* 计算器(同 topic/jisuan 列表;heat 也同那页) */
var CALC = [
{ q: '生娃到底要花多少钱?一键算清', h: 'topic/huafei/', heat: .118 },
{ q: '孕期该长多少斤?按身高体重算', h: '/yunqi-tizhong/', heat: .083 },
{ q: '胎儿现在多重?填 B 超数估一估', h: '/taier-guzhong/', heat: .071, gate: 15 },
{ q: '产假能休多少天?哪天回去上班', h: 'topic/zhichang/', heat: .062, cm: 1 } /* cm:带 calc=1,进去第一屏就是计算器(11-03) */
];
var QA = null, day = Math.floor((Date.now() + 288e5) / 864e5);
function band() { var p = window.__mbPreg || {}; return p.postpartum || !p.week ? 0 : (window.__mbQaBand ? window.__mbQaBand(p.week) : 0); }
function rows() {
var b = band(), p = window.__mbPreg || {}, wk = p.week && !p.postpartum ? p.week : 0, out = { qz: [], qa: [], js: [] };
var meta = window.GC_TESTS_META || [], pool = (QZ_POOL[b] || QZ_POOL[0]).map(function (id) { return meta.filter(function (x) { return x.id === id; })[0]; }).filter(Boolean);
for (var i = 0; i < Math.min(2, pool.length); i++) {
var t = pool[(day + i) % pool.length];
out.qz.push({ q: t.title, href: 'guangchang/ceshi/' + cleanQ({ id: t.id }), n: window.gcTested ? window.gcTested(t.id) : 0, u: '人测过' });
}
if (QA && QA.length) {
var skip = ['topic/huafei/', 'topic/ceshi/', 'topic/duiyou/', 'topic/zhichang/'];
var all = window.__mbQaPick ? window.__mbQaPick(QA, wk, 40) : QA;
all.filter(function (x) { return x.href && skip.indexOf(x.href) < 0; }).slice(0, 2).forEach(function (x) {
out.qa.push({ q: x.q, href: x.href + location.search + location.hash, n: window.__mbQaViews ? window.__mbQaViews(+x.heat || .03) : total() * (+x.heat || .03), u: '人看过' /* 11-03 同百问列表页口径 */ });
});
}
var cl = CALC.filter(function (x) { return !(x.gate && wk && wk < x.gate); });
if (b >= 5) cl = [cl[cl.length - 1]].concat(cl.slice(0, -1)); /* 孕 29 周起产假排前 */
cl.slice(0, 2).forEach(function (x) { out.js.push({ q: x.q, href: x.h + (x.cm ? (location.search ? location.search + '&' : '?') + 'calc=1' : location.search) + location.hash, n: total() * x.heat, u: '人算过' }); });
var r = [];
for (var k = 0; k < 2; k++) ['qz', 'qa', 'js'].forEach(function (c) { if (out[c][k]) r.push(Object.assign({ c: c }, out[c][k])); });
return { r: r, wk: wk };
}
var TAG = { qz: '测试', qa: '百问', js: '计算器' };
var KR_GO = { qz: '去测', qa: '去看', js: '去算' };
/* 11-04 用户:「前面的小图标换成字:测试用『测』、百问用『问』、计算器用『算』」(KN_IC 线稿图标留着备用) */
var KR_CH = { qz: '测', qa: '问', js: '算' };
var KN_IC = {
qz: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M9 3h6M10 3v6l-5 9a2 2 0 001.8 3h10.4a2 2 0 001.8-3l-5-9V3" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>',
qa: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h16v11H9l-5 4z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/><path d="M10 9.5a2 2 0 113 1.7c-.6.4-1 .8-1 1.5M12 14.5h.01" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg>',
js: '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="3" width="14" height="18" rx="2.5" fill="none" stroke="currentColor" stroke-width="2"/><path d="M8.5 7h7M8.5 12h.01M12 12h.01M15.5 12h.01M8.5 16h.01M12 16h.01M15.5 16h.01" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>'
};
function paint() {
nums();
var d = rows(), L = box.querySelector('.kit-list');
/* 11-03 用户:「这 6 条跟下面孕妈广场那 6 条太像,换个形式」→(便签墙试过,用户要回到一行一条的排列,只换样式)
一行一条,但不用名次数字和配图:左边按类型的彩色图标块(测试紫 / 百问绿 / 计算器橙),中间问题 + 类型·人数,右边 ›(11-03 用户嫌乱,样式收成一张白卡,见 kit.css)。 */
L.className = 'kit-list kit-rows';
L.innerHTML = d.r.map(function (x) {
return '<a class="kr ripple t-' + x.c + '" href="' + esc(x.href) + '"><span class="kr-ic kr-ch">' + KR_CH[x.c] + '</span>' +
'<span class="kr-m"><span class="kr-q">' + esc(x.q) + '</span><span class="kr-n"><em>' + TAG[x.c] + '</em>' + FIRE + '<b>' + wan(x.n) + '</b>' + x.u + '</span></span>' +
'<span class="kr-go">' + KR_GO[x.c] + '</span></a>';
}).join('');
}
/* ---------- 6 条下面:「大家刚刚在用」轮播 ----------
11-04 用户:「6 个问题底部放个轮播:谁谁谁测出自己是什么类型、谁谁谁算出养娃要花几十万……」
→ 一行一条,每 3.2 秒往上翻一条;测试结果取 tests.js 里真实的类型名,花费 / 产假 / 体重是计算器按常见输入能算出的数,百问取当前热门题。
昵称、头像、时间都是模拟(同「N 人测过」口径,AGENTS.md 12b,12-31 复核),点一下进对应那一项。 */
var TK_NM = ['小满妈妈', '糯米团子', '橙子不酸', '鹿鹿呀', '安安的妈', '豆包本包', '一只小葵', '阿圆', '栗子酱', '桃桃乌龙', '芝麻汤圆', '晴天娃娃',
'果冻布丁', '岁岁平安', '叮当妈', '饭团子', '奶糖不甜', '小鱼干', '朵朵妈咪', '暖暖', '布丁要长大', '花卷儿', '柚子茶', '星星的妈'];
var TK_AV = ['p02', 'm05', 'p05', 'm12', 'k04', 'p09', 'm16', 'p11', 'm07', 'k10', 'p03', 'm19', 'p13', 'm02', 'k06', 'p06', 'm10', 'p12', 'm21', 'p07', 'm04', 'k12', 'p14', 'm15'];
var TK_FIX = [
['qz', '测出自己是「松弛感满分型」妈妈', 't11'], ['js', '算了算,养到 18 岁大约要 <b>62.8 万</b>', 'topic/huafei/'],
['qz', '测出宝宝会是「好奇探险家」小性格', 't24'], ['js', '算出产假能休 <b>178 天</b>(广东)', 'topic/zhichang/'],
['qz', '测出自己是「性价比务实玩家」', 't29'], ['js', '算出孕期该长 <b>16~28 斤</b>', '/yunqi-tizhong/'],
['qz', '测出自己是「计划表女王型」', 't11'], ['js', '算出生娃头一年大概花 <b>9.6 万</b>', 'topic/huafei/'],
['qz', '测出宝宝会是「温柔小暖阳」', 't24'], ['js', '算了算,养到 18 岁大约要 <b>48.5 万</b>', 'topic/huafei/'],
['qz', '测出自己是「细节控守护型」', 't18'], ['js', '算出产假能休 <b>158 天</b>(浙江)', 'topic/zhichang/']
];
var TK_AGO = ['刚刚', '刚刚', '1 分钟前', '2 分钟前', '刚刚', '3 分钟前'];
var tk = null, tkI = 0, tkT = 0;
function tkItems() {
var qa = (QA || []).filter(function (x) { return x.href && !/^topic\/(huafei|ceshi|duiyou|zhichang)\/$/.test(x.href); }).slice(0, 6), out = [];
TK_FIX.forEach(function (f, i) {
out.push(f);
if (i % 2 && qa[(i - 1) / 2]) out.push(['qa', '看了「' + esc(qa[(i - 1) / 2].q) + '」', qa[(i - 1) / 2].href]);
});
return out.map(function (f, i) {
var j = (i + day) % TK_NM.length, h = f[0] === 'qz' ? 'guangchang/ceshi/' + cleanQ({ id: f[2] })
: f[0] === 'qa' ? f[2] + location.search + location.hash
: f[2] + (f[2] === 'topic/zhichang/' ? (location.search ? location.search + '&' : '?') + 'calc=1' : location.search) + location.hash;
return '<a class="tk-i t-' + f[0] + '" href="' + esc(h) + '"><img src="assets/img/mom/' + TK_AV[j] + '.jpg" alt="" loading="lazy" width="22" height="22">' +
'<span class="tk-t"><em>' + esc(TK_NM[j]) + '</em>' + (f[0] === 'qa' ? f[1] : f[1]) + '</span><i class="tk-c">' + KR_CH[f[0]] + '</i><small>' + TK_AGO[i % TK_AGO.length] + '</small></a>';
});
}
function ticker() {
var pn = box.querySelector('.kit-pn'); if (!pn) return;
var it = tkItems();
if (!tk) { tk = document.createElement('div'); tk.className = 'kit-tk'; tk.setAttribute('aria-live', 'off'); pn.appendChild(tk); }
tk.innerHTML = '<span class="tk-dot" aria-hidden="true"></span><div class="tk-w">' + it.join('') + '</div>';
var els = tk.querySelectorAll('.tk-i'); tkI = 0; if (els[0]) els[0].classList.add('on');
clearInterval(tkT);
tkT = setInterval(function () {
if (document.hidden || !tk.offsetParent) return;
var a = els[tkI]; tkI = (tkI + 1) % els.length; var b = els[tkI];
a.classList.remove('on'); a.classList.add('up');
setTimeout(function () { a.classList.remove('up'); }, 450);
b.classList.add('on');
}, 3200);
}
fetch('assets/data/qa/hot.json?v=' + V).then(function (r) { return r.ok ? r.json() : []; }).catch(function () { return []; })
.then(function (a) { QA = Array.isArray(a) ? a : []; paint(); ticker(); });
if (!window.gcTested) { var ms = document.createElement('script'); ms.src = 'guangchang/ceshi/meta.js?v=' + V; ms.onload = paint; document.body.appendChild(ms); }
window.addEventListener('mb-preg', paint);
setTimeout(paint, 3000); /* 「此刻」总人数接口晚到时按后台配置的人数重算一次 */
paint();
box.addEventListener('click', function (e) {
var b = e.target.closest('.kit-e'); if (!b) return;
try { navigator.vibrate && navigator.vibrate(8); } catch (x) {}
var k = b.dataset.k;
if (k === 'qz') location.href = 'guangchang/' + cleanQ({ c: 'ceshi' });
else if (k === 'js') location.href = 'topic/jisuan/' + cleanQ();
else if (window.__mbOpenQA) window.__mbOpenQA('孕期百问');
});
})();