Compare commits
40 Commits
5f2db65aad
...
51828a097c
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
51828a097c | ||
|
|
8b84526dec | ||
|
|
9d966a99c0 | ||
|
|
687669abe7 | ||
|
|
bc65bea7e9 | ||
|
|
6bf65ef25d | ||
|
|
52ff1ab718 | ||
|
|
5f6f7abda3 | ||
|
|
e8df9dd389 | ||
|
|
723ad18f3c | ||
|
|
580f5144a7 | ||
|
|
35717e8793 | ||
|
|
abacdfb3c2 | ||
|
|
679cf7f368 | ||
|
|
a79912d67b | ||
|
|
0cc1f8835d | ||
|
|
19fb605567 | ||
|
|
d8435e3dc8 | ||
|
|
d5b62f88a4 | ||
|
|
954e381604 | ||
|
|
563b0943cf | ||
|
|
85e1aeb3d5 | ||
|
|
d192f42e36 | ||
|
|
f1cedd1e34 | ||
|
|
55012dc1ff | ||
|
|
3626ce7d78 | ||
|
|
346d548fed | ||
|
|
c0c3522dd3 | ||
|
|
c804214ea9 | ||
|
|
74e17feed2 | ||
|
|
85cb4a6a14 | ||
|
|
afe14f1acb | ||
|
|
4b13d06f8f | ||
|
|
aee7371535 | ||
|
|
884732c0a9 | ||
|
|
f3072e5617 | ||
|
|
2fbb76c60b | ||
|
|
4cbad40d1f | ||
|
|
d89431aae1 | ||
|
|
42794d2ed9 |
@@ -115,6 +115,7 @@ nginx:`root /opt/app` + `Cache-Control: no-cache` + `etag on` —— 缓存靠
|
||||
- 决策人:产品负责人(用户本人),2026-11-02 拍板「准备 1000 个头像和名字,每篇评论至少 20 条以上,也会有作者回复」;已书面告知虚假宣传风险(《反不正当竞争法》第 8 条),且比模拟数字更敏感。
|
||||
- 期限:**最晚 2026-12-31** 复核;真实评论上线后,有真实评论的帖子要先撤掉模拟评论,不得混排冒充。
|
||||
- 2026-11-02 扩展:104 个专题页底部「大家怎么说」的预置评论 `topic/_shared/cm/<slug>.json`(每页 10~14 条 + 楼中楼,共约 1200 条,人设取自同一批 1000 个),由 `topic/_shared/tools.js` 渲染;用户自己发的评论只存本机 `mmbox.tp.v1`,不上传。同一期限复核。
|
||||
- 2026-11-07 扩展:孕妈广场「全部」信息流的置顶笔记「孕妈悄悄话」(id `wh-20260928`,按每周话题一篇)下 100 条模拟祝福评论 + 18 条楼中楼,存在同一个 `cm.json`,由 `tools/whcm/build.py` 从 `lines.txt` 生成(人设取同一批,主评论限孕 24 周以上)。用户 11-07 原话「评论里边还是按照正常评论都给我放上 100 个」。模拟评论**不得**挂上星空(挂星只认真实且审核通过的)。同一期限复核。
|
||||
- 边界:只用于占位帖子的详情页评论区;真实投稿(`n.real`)一律不配模拟评论;这 1000 个人设不得出现在投稿审核、排行榜、通知等任何其他地方。
|
||||
|
||||
13. **跳其它 H5 应用 / 调第三方接口一律同源:只写相对路径 `/<slug>/`,不写域名**(2026-10-09 用户定)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<!-- 11-04 UGC 总开关关着时本页不展示,直接回首页(js/ugc.js) -->
|
||||
<script src="../js/login-gate.js?v=26110605"></script>
|
||||
<script src="../js/login-gate.js?v=26110610"></script>
|
||||
<script src="../js/ugc.js?v=26110464" data-guard></script>
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
@@ -66,6 +66,40 @@ button{font:inherit;color:inherit;border:0;background:none;padding:0;cursor:poin
|
||||
.qf{position:relative;display:block;width:calc(100% - 24px);height:148px;margin:2px 12px 0;border-radius:20px;overflow:hidden;text-align:left;
|
||||
background:radial-gradient(120% 140% at 82% 100%,#3B3A86 0%,#252A63 38%,#161B3F 75%);box-shadow:0 12px 26px -14px rgba(30,30,90,.7)}
|
||||
.qf:active{transform:scale(.985)}
|
||||
/* 11-07 置顶笔记「孕妈悄悄话」(用户:首页不放,在广场内容里发一条置顶笔记,点了进星空页):
|
||||
右列第一张,样子是一张笔记卡 —— 封面换成夜空(星星一闪一闪 + 偶尔一道流星)+ 本周话题大字 + 一句轮播的悄悄话 */
|
||||
.nc.pin .pcv,.dcv .pcv{position:relative;height:calc(var(--cw, 175px) * 1.25);overflow:hidden;color:#fff;
|
||||
background:radial-gradient(120% 70% at 85% 0%,#5A3B72 0%,transparent 60%),radial-gradient(90% 60% at 0% 100%,#4A2C55 0%,transparent 70%),linear-gradient(180deg,#191634 0%,#241D44 55%,#2F2148 100%)}
|
||||
.pcv .psk{position:absolute;inset:0;pointer-events:none}
|
||||
.pcv .psk i{position:absolute;width:6px;height:6px;margin:-3px 0 0 -3px;opacity:.85;filter:drop-shadow(0 0 2px rgba(255,255,255,.7));
|
||||
background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z' fill='%23FFFFFF'/%3E%3C/svg%3E") center/contain no-repeat;animation:pTw 2.6s ease-in-out infinite}
|
||||
.pcv .psk i.b{width:11px;height:11px;margin:-5.5px 0 0 -5.5px;opacity:1;filter:drop-shadow(0 0 4px rgba(255,255,255,.9))}
|
||||
@keyframes pTw{0%,100%{opacity:.9;transform:scale(1)}50%{opacity:.2;transform:scale(.55)}}
|
||||
.pcv .pmt{position:absolute;top:8%;left:78%;width:80px;height:2px;border-radius:2px;opacity:0;transform-origin:0 50%;
|
||||
background:linear-gradient(90deg,#fff,rgba(255,255,255,.7) 30%,rgba(255,255,255,0));box-shadow:0 0 8px 1px rgba(255,255,255,.8);animation:pMt 6s linear infinite 1.2s}
|
||||
.pcv .pmt::before{content:"";position:absolute;left:-2px;top:-1.5px;width:5px;height:5px;border-radius:50%;background:#fff;box-shadow:0 0 6px 2px #fff}
|
||||
.pcv .pmt.m2{top:30%;left:96%;width:60px;animation-duration:9s;animation-delay:4.2s}
|
||||
@keyframes pMt{0%{opacity:0;transform:translate(0,0) rotate(-28deg)}2%{opacity:1}12%{opacity:0;transform:translate(-130px,70px) rotate(-28deg)}100%{opacity:0;transform:translate(-130px,70px) rotate(-28deg)}}
|
||||
@media (prefers-reduced-motion:reduce){.pcv .psk i,.pcv .pmt{animation:none}}
|
||||
.pcv .ptop{position:absolute;left:8px;top:8px;z-index:1;height:19px;padding:0 7px;border-radius:6px;background:linear-gradient(120deg,#FFE2B8,#FFB98F);color:#4A2E1E;font-size:10.5px;font-style:normal;font-weight:900;line-height:19px;letter-spacing:.5px}
|
||||
.pcv .pbig{position:absolute;left:12px;right:12px;bottom:40px;z-index:1}
|
||||
.pcv .pbig small{display:block;font-size:10.5px;font-weight:700;letter-spacing:1px;color:rgba(255,231,199,.75)}
|
||||
.pcv .pbig b{display:block;margin-top:4px;font-size:20px;font-weight:900;line-height:1.3;letter-spacing:.5px;color:#FFE7C7;text-shadow:0 2px 10px rgba(20,10,40,.6)}
|
||||
.pcv .pq{position:absolute;left:12px;right:12px;bottom:12px;z-index:1;margin:0;font-size:11.5px;color:rgba(255,244,232,.8);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;transition:opacity .26s,transform .26s}
|
||||
.pcv .pq.out{opacity:0;transform:translateY(5px)}
|
||||
.nb .ntg.k-whs{background:#F0EBFA;color:#7A5BB8}
|
||||
.nc.pin .nf .pav{flex:none;display:grid;place-items:center;width:18px;height:18px;border-radius:50%;background:linear-gradient(135deg,#3E2F5E,#6B4AA8);color:#FFE2B8;font-size:10px;font-style:normal}
|
||||
/* 置顶笔记详情页:夜空封面 + 「去星空看看」;不要点赞/收藏/关注(这篇是官方置顶,不是某个妈妈的帖子) */
|
||||
.dcv .pcv{height:min(78vw,340px)}
|
||||
.dcv .pcv .pbig{bottom:auto;top:30%}
|
||||
.dcv .pcv .pbig small{font-size:12px}
|
||||
.dcv .pcv .pbig b{font-size:28px}
|
||||
.dcv .pcv .pq{bottom:64px;font-size:13px}
|
||||
.dcv .pcv .ptop{left:14px;top:14px}
|
||||
.pstar{position:absolute;left:14px;bottom:16px;z-index:2;display:inline-flex;align-items:center;gap:4px;height:36px;padding:0 16px;border:0;border-radius:99px;font:inherit;font-size:14px;font-weight:800;color:#4A2E1E;background:linear-gradient(120deg,#FFE2B8,#FFB98F);box-shadow:0 6px 16px -6px rgba(255,185,143,.8)}
|
||||
.pstar:active{transform:scale(.97)}
|
||||
#dt.is-pin #blk,#dt.is-pin #bfv,#dt.is-pin #dfo{display:none}
|
||||
.nc.pin .nf .pgo{margin-left:auto;flex:none;font-size:11px;font-weight:800;color:#7A5BB8}
|
||||
.qf-st{position:absolute;inset:0;pointer-events:none;opacity:.9;
|
||||
background-image:radial-gradient(1.4px 1.4px at 12% 20%,#fff,transparent),radial-gradient(1px 1px at 28% 70%,#fff,transparent),radial-gradient(1.2px 1.2px at 46% 14%,#FFE9B0,transparent),
|
||||
radial-gradient(1px 1px at 54% 82%,#fff,transparent),radial-gradient(1.5px 1.5px at 62% 30%,#fff,transparent),radial-gradient(1px 1px at 8% 88%,#FFE9B0,transparent),radial-gradient(1.2px 1.2px at 40% 46%,#fff,transparent);
|
||||
@@ -450,6 +484,19 @@ button{font:inherit;color:inherit;border:0;background:none;padding:0;cursor:poin
|
||||
/* 11-05 整合:投稿奖励一行(原在热门话题列表头)+ 选中标签后的热门话题 */
|
||||
.rww{margin:14px 12px 0}
|
||||
.rww .rwl{margin:0}
|
||||
/* 11-07 用户:祈福树下面「投稿被选中得 ¥30…」一条 → 改成轮播「哪些妈妈的笔记上了广场」:真实投稿(审核通过)排前,
|
||||
不够时用广场里点赞高的笔记补;只说「上了广场 / 入选精选」,不写某人领了多少钱。右边「我也投稿」= 原来的规则弹层 */
|
||||
.rwc{display:flex;align-items:center;gap:8px;height:40px;padding:0 12px;border-radius:12px;background:linear-gradient(100deg,#FFF1E8,#FFE6E6)}
|
||||
.rwc>.ic{flex:none;width:16px;height:16px;color:#F2553A}
|
||||
.rwv{position:relative;flex:1;min-width:0;height:40px;overflow:hidden}
|
||||
.rwi{position:absolute;left:0;right:0;top:0;display:flex;align-items:center;gap:6px;height:40px;border:0;background:none;padding:0;font:inherit;text-align:left;color:#8A4B36;font-size:12px;
|
||||
transition:transform .4s ease,opacity .4s ease}
|
||||
.rwi.in{transform:translateY(100%);opacity:0}.rwi.out{transform:translateY(-100%);opacity:0}
|
||||
.rwi img{flex:none;width:20px;height:20px;border-radius:50%;object-fit:cover;background:#FFE3D3}
|
||||
.rwi span{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.rwi span b{font-weight:800;color:#5A3226}
|
||||
.rwi span em{font-style:normal;font-weight:800;color:#F2394A;margin:0 3px}
|
||||
.rwb{flex:none;height:26px;padding:0 10px;border:0;border-radius:99px;background:linear-gradient(135deg,#FF8A65,#F2553A);color:#fff;font:inherit;font-size:11.5px;font-weight:800}
|
||||
.gtp{display:flex;flex-direction:column;align-items:flex-start;gap:6px;margin:2px 14px 4px;padding:10px 12px;border-radius:14px;background:#fff;box-shadow:0 6px 16px -14px rgba(120,60,40,.35)}
|
||||
.gtp[hidden]{display:none}
|
||||
.gtp-h{font-size:11.5px;font-weight:800;color:#F2553A}
|
||||
@@ -931,6 +978,7 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
<div id="sheet" role="dialog" aria-modal="true"><i class="sg"></i><div class="sbd" id="sbd"></div></div>
|
||||
<div class="toast" id="toast"></div>
|
||||
|
||||
<script src="../js/wh-themes.js?v=26110609"></script> <!-- 11-07 悄悄话每周话题(与首页共用) -->
|
||||
<script src="topics.js?v=26110487"></script>
|
||||
<script src="data.js?v=26110487"></script>
|
||||
<script src="feed.js?v=26110487"></script>
|
||||
@@ -1005,7 +1053,7 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
var CMD = null, CMERR = false;
|
||||
/* 头像编码:数字 = av/NNNN.webp(旧代码头像);'av:c012' = av/c012.webp(卡通/可爱卡通);'/…' = 站内绝对路径(内置头像库 /_bridge/avatars/);其余 = assets/img/mom/xxx.jpg */
|
||||
var avP = function (a) { return typeof a === 'number' ? 'av/' + ('000' + a).slice(-4) + '.webp' : a.indexOf('av:') === 0 ? 'av/' + a.slice(3) + '.webp' : a.charAt(0) === '/' ? a : '../assets/img/mom/' + a + '.jpg'; };
|
||||
fetch('cm.json?v=26110487').then(function (r) { return r.json(); }).then(function (j) {
|
||||
fetch('cm.json?v=26110631').then(function (r) { return r.json(); }).then(function (j) {
|
||||
CMD = j; if (openId && NBY[openId]) { renderComments(NBY[openId]); syncBar(NBY[openId]); }
|
||||
}).catch(function () { CMERR = true; if (openId && NBY[openId]) renderComments(NBY[openId]); });
|
||||
function commentsOf(n) {
|
||||
@@ -1031,9 +1079,10 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
/* 11-03 用户:评论要后台审核通过才对外公开。服务器 /plaza/comments:list = 别人已通过的;mine = 我在这篇下发的(含审核中 / 未通过)。
|
||||
ME.cm 里没有 id 的是 11-03 以前只存本机的旧评论,从没公开过,照旧只给自己看(标「仅自己可见」)。 */
|
||||
var RC = {};
|
||||
/* 11-07 用户:评论、回复一定要审核通过才显示,自己也不显示「审核中」—— 只留已通过的;本机旧评论(从没审过)也不再显示 */
|
||||
var myCm = function (id) {
|
||||
var old = (ME.cm[id] || []).filter(function (c) { return !c.id; }).map(function (c) { return { t: c.t, ts: c.ts, s: c.s, st: 'local' }; });
|
||||
var srv = RC[id] ? RC[id].mine.map(function (c) { return { id: c.id, t: c.text, ts: c.at, s: c.stage, st: c.status, why: c.reason, parent: c.parent, to: c.parentName, img: c.images }; }) : [];
|
||||
var old = [];
|
||||
var srv = RC[id] ? RC[id].mine.filter(function (c) { return c.status === 'approved'; }).map(function (c) { return { id: c.id, t: c.text, ts: c.at, s: c.stage, st: c.status, why: c.reason, parent: c.parent, to: c.parentName, img: c.images }; }) : [];
|
||||
return old.concat(srv.reverse());
|
||||
};
|
||||
var realCm = function (id) { return RC[id] ? RC[id].list : []; };
|
||||
@@ -1172,7 +1221,51 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
'<span class="qf-fly" aria-hidden="true"><i>福</i><i>安</i><i>福</i><i>康</i></span>' +
|
||||
'<span class="qf-tx"><small><i></i>宝宝祈福树</small><b><i>#</i>为宝宝挂一张福牌<i>#</i></b><em>集齐五福,愿宝宝平平安安</em>' +
|
||||
'<span class="qf-go">去祈福<svg class="ic"><use href="#i-right"/></svg></span></span></button>';
|
||||
} /* 11-07 用户:「首页就不放了,在内容页里放,发一个笔记置顶,点击就进到这个页面」→ 「全部」信息流右列第一张置顶笔记卡。
|
||||
有孕周(首页写的 mmbox.gcwk.v1,≥4 周、没生)且没退出登录才出;数据同首页 GET /whisper?openid=&week=,回来后卡里轮播真实的悄悄话,
|
||||
拉不到就只显示话题,不造内容。星空全屏页在首页 → 点了跳首页带 ?wh=1,首页直接打开星空页,关掉回到这里。 */
|
||||
var PIN_WK = (function () { try { var g = JSON.parse(localStorage.getItem('mmbox.gcwk.v1') || 'null'); return g && !g.pp && g.w ? +g.w : 0; } catch (e) { return 0; } })();
|
||||
var PIN_ON = PIN_WK >= 4 && !(window.mbGate && window.mbGate.out);
|
||||
var WHD = null, whRi = 0, whT = null;
|
||||
/* 置顶笔记本身:按每周话题一篇(id = wh-话题起始日),留言走广场评论那套(先审后发,后台「评论审核」页签);
|
||||
模拟祝福评论在 cm.json 的 c['wh-20260928'](100 条,tools/whcm/,AGENTS.md 12c 扩展) */
|
||||
var PIN_TH = window.MB_WH_THEME ? window.MB_WH_THEME() : null;
|
||||
var PIN_ID = 'wh-' + (PIN_TH ? PIN_TH.from.replace(/-/g, '') : '0');
|
||||
var PIN = NBY[PIN_ID] = { id: PIN_ID, pin: 1, title: '孕妈悄悄话 · ' + (PIN_TH ? PIN_TH.name : ''), author: '孕妈悄悄话', stage: '', likes: 0 };
|
||||
function pinCoverHTML(th, l, sky, big) {
|
||||
return '<div class="pcv"><span class="psk" aria-hidden="true">' + sky + '<b class="pmt"></b><b class="pmt m2"></b></span><i class="ptop">置顶</i>' +
|
||||
'<div class="pbig"><small>' + (th ? '本周话题' : '孕妈悄悄话') + '</small><b>' + (th ? (th.lines || [th.name]).map(esc).join('<br>') : '说给宝宝听的悄悄话') + '</b></div>' +
|
||||
'<p class="pq" id="' + (big ? 'pinRl2' : 'pinRl') + '">' + (l.length ? '“' + esc(l[0].text) + '”' : '点一颗星星,看看她说了啥') + '</p>' +
|
||||
(big ? '<button type="button" class="pstar" data-star="1">去星空看看<svg class="ic" style="width:14px;height:14px"><use href="#i-right"/></svg></button>' : '') + '</div>';
|
||||
}
|
||||
function pinHTML() {
|
||||
var th = (WHD && WHD.theme && WHD.theme.name) ? WHD.theme : (window.MB_WH_THEME ? window.MB_WH_THEME() : null);
|
||||
var l = WHD ? (WHD.list || []).filter(function (x) { return !x.mine; }) : [];
|
||||
var sky = pinSky();
|
||||
return '<button type="button" class="nc pin" id="whPin" data-id="' + PIN_ID + '" aria-label="置顶:孕妈悄悄话' + (th ? ',本周话题' + esc(th.name) : '') + '"><div class="cvw">' + pinCoverHTML(th, l, sky) + '</div>' +
|
||||
'<div class="nb"><h3>每颗星星,都是一个妈妈说的话</h3><span class="ntg k-whs">#孕妈悄悄话</span>' +
|
||||
'<div class="nf"><i class="pav" aria-hidden="true"><svg viewBox="0 0 20 20" width="10" height="10"><path d="M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z" fill="currentColor"/></svg></i><span class="nm">孕妈悄悄话</span><span class="pgo">去看看 ›</span></div></div></button>';
|
||||
}
|
||||
var PIN_SKY = [[14,14,0,-1.2,2.4],[38,8,0,-.3,2],[62,20,1,-2.4,2.8],[86,12,0,-3.1,2.2],[24,34,1,-3.7,2.6],[50,40,0,-1.6,3.2],[80,32,0,-2.2,2.6],[10,52,0,-.8,3],[70,50,1,-1.9,2.4],[90,58,0,-2.8,3]];
|
||||
function pinSky() { return PIN_SKY.map(function (k) { return '<i' + (k[2] ? ' class="b"' : '') + ' style="left:' + k[0] + '%;top:' + k[1] + '%;animation-delay:' + k[3] + 's;animation-duration:' + k[4] + 's"></i>'; }).join(''); }
|
||||
function whRoll() {
|
||||
clearInterval(whT); whRi = 0;
|
||||
var els = ['pinRl', 'pinRl2'].map(function (x) { return document.getElementById(x); }).filter(Boolean), l = WHD ? (WHD.list || []).filter(function (x) { return !x.mine; }) : [];
|
||||
if (!els.length || !l.length) return;
|
||||
els.forEach(function (el) { el.textContent = '“' + l[0].text + '”'; });
|
||||
if (l.length < 2) return;
|
||||
whT = setInterval(function () {
|
||||
var es = ['pinRl', 'pinRl2'].map(function (x) { return document.getElementById(x); }).filter(Boolean);
|
||||
if (!es.length) { clearInterval(whT); return; } if (document.hidden) return;
|
||||
whRi = (whRi + 1) % l.length; es.forEach(function (e2) { e2.classList.add('out'); });
|
||||
setTimeout(function () { es.forEach(function (e2) { e2.textContent = '“' + l[whRi].text + '”'; e2.classList.remove('out'); }); }, 260);
|
||||
}, 4000);
|
||||
}
|
||||
if (PIN_ON) fetch('/api/mp/public/mama-box/whisper?openid=' + encodeURIComponent(OPENID || '') + '&week=' + PIN_WK)
|
||||
.then(function (r) { return r.ok ? r.json() : null; })
|
||||
.then(function (j) { var r = j && j.code === 200 && j.result; if (r && r.show && (r.list || []).length) { WHD = r; whRoll(); } })
|
||||
.catch(function () {});
|
||||
|
||||
/* 分类小 banner:家庭婚姻 / 职场 / 经验 点了 = 参与本类最热话题(投稿规则弹层);测一测 点了 = 进最热的那个测试 */
|
||||
var CBN = {
|
||||
family: ['家庭婚姻', '孕期的家,也需要用心经营', '和老公、婆婆、爸妈的那些事', '我也来聊聊'],
|
||||
@@ -1573,6 +1666,33 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
bx.hidden = !tl.length;
|
||||
bx.innerHTML = '<span class="gtp-h">热门话题</span>' + tl.map(function (x) { return '<button type="button" data-t="' + x.id + '">#' + esc(x.name) + '#</button>'; }).join('');
|
||||
}
|
||||
/* ---------- 「谁的笔记上了广场」轮播(11-07) ---------- */
|
||||
var rwI = 0, rwT = null;
|
||||
function rwList() {
|
||||
var real = NOTES.filter(function (n) { return n.real; }).slice(0, 10);
|
||||
var pick = NOTES.filter(function (n) { return !n.real && n.photo; }).sort(function (a, b) { return b.likes - a.likes; });
|
||||
var seen = {}, mock = [];
|
||||
pick.forEach(function (n) { if (mock.length < 12 && !seen[n.author]) { seen[n.author] = 1; mock.push(n); } });
|
||||
return real.concat(mock);
|
||||
}
|
||||
function rwItem(k) {
|
||||
var L = rwList(); if (!L.length) return '';
|
||||
var n = L[k % L.length], tt = n.title.length > 14 ? n.title.slice(0, 13) + '…' : n.title;
|
||||
return '<button type="button" class="rwi" data-id="' + esc(n.id) + '"><img src="' + esc(avOf(n)) + '" alt="" loading="lazy"><span><b>' + esc(n.author) + '</b>' +
|
||||
(n.real ? '<em>投稿上了广场</em>' : '<em>入选精选</em>') + '《' + esc(tt) + '》</span></button>';
|
||||
}
|
||||
function rwRun() {
|
||||
clearInterval(rwT); rwI = 0;
|
||||
if (rwList().length < 2) return;
|
||||
rwT = setInterval(function () {
|
||||
var box = document.getElementById('rwv'); if (!box) { clearInterval(rwT); return; }
|
||||
if (document.hidden || openId) return;
|
||||
var old = box.firstElementChild; rwI++;
|
||||
box.insertAdjacentHTML('beforeend', rwItem(rwI));
|
||||
var nw = box.lastElementChild; nw.classList.add('in'); void nw.offsetWidth;
|
||||
nw.classList.remove('in'); if (old) { old.classList.add('out'); setTimeout(function () { if (old.parentNode) old.remove(); }, 420); }
|
||||
}, 3200);
|
||||
}
|
||||
function renderHead() {
|
||||
var t = TBY[cur], el = $('#head'), fh = $('#fh');
|
||||
var fl0 = document.getElementById('luckFl'); if (fl0) fl0.classList.remove('on');
|
||||
@@ -1582,7 +1702,8 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
标签作为唯一入口(末尾加「全部话题 ›」);选中标签时标签下露出这一类的 2~3 个热门话题(用户同意);投稿奖励一行挪到「大家在聊」上面。
|
||||
话题本身不变:投稿选话题、帖子卡上的 #话题#、话题详情页照旧。分类视图(catF)仍是原来的热门话题列表。 */
|
||||
el.innerHTML = qifuHTML() +
|
||||
'<div class="rww"><button type="button" class="rwl" data-rule="1"><svg class="ic"><use href="#i-gift"/></svg><span>投稿被选中得 ' + REWARD + '</span><b>怎么参与 ›</b></button></div>';
|
||||
'<div class="rww"><div class="rwc"><div class="rwv" id="rwv">' + rwItem(0) + '</div></div></div>'; /* 11-07 用户:去掉礼物图标和「我也投稿」 */
|
||||
rwRun();
|
||||
fh.className = 'shtg';
|
||||
fh.innerHTML = '<div class="sh"><h2>大家在聊</h2><small>孕妈们的分享</small></div>' +
|
||||
'<div class="gtg"><button type="button" data-tag=""' + (tagF ? '' : ' class="on"') + '>全部</button>' +
|
||||
@@ -1660,7 +1781,7 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
11-03 用户:「笔记里测一测减少一些,已经有测一测模块了」→ 原来第 3 位起每 8 张插 1 张(约 20 张),改成第 6 位起每 30 张插 1 张、最多 3 张 */
|
||||
var csq = CS_MIX.concat(byHot(TM).map(function (x) { return x.id; }).filter(function (id) { return CS_MIX.indexOf(id) < 0; }));
|
||||
list.forEach(function (n, k) { if (k >= 6 && (k - 6) % 30 === 0 && ci < 3) { var tid = csq[ci++ % csq.length]; if (TMBY[tid]) mixed.push({ test: TMBY[tid] }); } mixed.push(n); });
|
||||
list = [{ mine: 1 }].concat(mixed);
|
||||
list = [{ mine: 1 }].concat(PIN_ON ? [{ pin: 1 }] : [], mixed); /* 11-07 置顶笔记「孕妈悄悄话」 */
|
||||
}
|
||||
/* 11-03 用户:切分类有点卡。原来每张卡塞进列里再 offsetHeight 量高,每量一次整页重排;85 张卡在中端机上卡 0.5 秒以上,正好卡在滑入动画中间。
|
||||
现在:帖子卡高度直接算(封面 = 列宽 × 比例,底部 = 标题 1/2 行 + 作者行,和 .nc 样式对应),只有「我的投稿 / 收藏 / 测试卡」这几种
|
||||
@@ -1672,6 +1793,7 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
list.forEach(function (n) {
|
||||
if (n.mine) specials.push(myCardHTML(), favCardHTML());
|
||||
else if (n.test) specials.push(testCardHTML(n.test, dn));
|
||||
else if (n.pin) specials.push(pinHTML());
|
||||
});
|
||||
var spH = [];
|
||||
if (specials.length) {
|
||||
@@ -1686,6 +1808,7 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
function add(html, c, h) { out[c].push(anim(html, k2++)); hs[c] += h + 8; }
|
||||
list.forEach(function (n) {
|
||||
if (n.mine) { add(specials[si], 0, spH[si++]); add(specials[si], 0, spH[si++]); return; }
|
||||
if (n.pin) { add(specials[si], 1, spH[si++]); lastTx[1] = false; return; } /* 右列第一张 */
|
||||
if (n.test) { var tc0 = hs[0] <= hs[1] ? 0 : 1; add(specials[si], tc0, spH[si++]); lastTx[tc0] = false; return; }
|
||||
nk++;
|
||||
var c = hs[0] <= hs[1] ? 0 : 1, sty = !lastTx[c] && n.photo ? (txN[c]++ % 2 ? 'tg' : 'ov') : '';
|
||||
@@ -1693,6 +1816,7 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
var r = n.ratio; if (n.photo && sty === 'ov') r = Math.max(r, 1.33); else if (n.photo && sty === 'tg') r = Math.max(r, 1.2);
|
||||
add(cardHTML(n, !cur, sty), c, estH(n, r));
|
||||
});
|
||||
if (PIN_ON) setTimeout(whRoll, 0);
|
||||
var FIRST = 14, gen = renderFeed.gen = (renderFeed.gen || 0) + 1;
|
||||
cols[0].innerHTML = out[0].slice(0, FIRST).join(''); cols[1].innerHTML = out[1].slice(0, FIRST).join('');
|
||||
if (out[0].length > FIRST || out[1].length > FIRST) setTimeout(function () {
|
||||
@@ -1821,6 +1945,7 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
var dc = e.target.closest('#csvGo'); if (dc) { csdRoll(dc); return; }
|
||||
var j = e.target.closest('[data-join]'); if (j) { haptic(); openRule(j.dataset.join); return; }
|
||||
if (e.target.closest('[data-rule]')) { openRule(''); return; }
|
||||
var rw = e.target.closest('.rwi[data-id]'); if (rw) { haptic(); openNote(rw.dataset.id, true); return; }
|
||||
if (e.target.closest('[data-all]')) { openTopics(); return; }
|
||||
var b = e.target.closest('[data-t]'); if (b) { haptic(); selectTopic(b.dataset.t); }
|
||||
});
|
||||
@@ -1942,8 +2067,17 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
$('#bcm span').textContent = cfmt(cmN(n));
|
||||
if (popK) { var b = popK === 'lv' ? lv : fv; b.classList.remove('pop'); void b.offsetWidth; b.classList.add('pop'); }
|
||||
}
|
||||
/* 先把本页地址换成不带 ?n= 的广场列表,星空页关掉 history.back() 回到列表,不会再弹回星空 */
|
||||
function goStar() {
|
||||
var q = new URLSearchParams(location.search); ['t', 'n', 'c'].forEach(function (k) { q.delete(k); });
|
||||
var qs = q.toString();
|
||||
try { history.replaceState(null, '', location.pathname + (qs ? '?' + qs : '') + location.hash); } catch (e) {}
|
||||
q.set('wh', '1'); location.href = '../?' + q + location.hash;
|
||||
}
|
||||
function openNote(id, push) {
|
||||
var n = NBY[id]; if (!n) return;
|
||||
if (n.pin) return goStar(); /* 11-07 用户:点置顶笔记直接进星空页,留言在星空页下面(openPin 详情页不再用) */
|
||||
$('#dt').classList.remove('is-pin');
|
||||
var t = TBY[n.t];
|
||||
openId = id;
|
||||
$('#dav').src = avOf(n);
|
||||
@@ -1969,6 +2103,31 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
if (push) { history.pushState({ gcNote: id }, '', setQuery(cur, id)); pushed = true; }
|
||||
else history.replaceState(history.state, '', setQuery(cur, id));
|
||||
}
|
||||
/* 11-07 置顶笔记「孕妈悄悄话」详情:夜空封面(点「去星空看看」进首页星空页)+ 说明 + 留言区(同普通笔记,先审后发) */
|
||||
var PIN_PH = '对宝宝说一句悄悄话…';
|
||||
function openPin(n, push) {
|
||||
openId = n.id;
|
||||
var th = PIN_TH, l = WHD ? (WHD.list || []).filter(function (x) { return !x.mine; }) : [];
|
||||
$('#dt').classList.add('is-pin');
|
||||
$('#dav').src = 'data:image/svg+xml,' + encodeURIComponent('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"><defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#3E2F5E"/><stop offset="1" stop-color="#6B4AA8"/></linearGradient></defs><circle cx="20" cy="20" r="20" fill="url(#g)"/><path transform="translate(10 10)" d="M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z" fill="#FFE2B8"/></svg>');
|
||||
$('#dnm').textContent = '孕妈悄悄话';
|
||||
$('#dst').textContent = th ? '本周话题 · ' + th.name : '';
|
||||
$('#dsc').innerHTML = '<div class="dcv">' + pinCoverHTML(th, l, pinSky(), true) + '</div>' +
|
||||
'<div class="dbody"><h2>每颗星星,都是一个妈妈说的话</h2><div class="tx">' +
|
||||
'<p>这周的话题是「' + esc(th ? th.name : '说给宝宝听的悄悄话') + '」。' + esc(th && th.tip ? th.tip + '。' : '') + '</p>' +
|
||||
'<p>在下面留言区写一句,审核通过后大家都能看到。点赞多的悄悄话会挂上星空,变成一颗星星,同孕周的妈妈点一下,就能看到它像流星一样划过。</p>' +
|
||||
'<p>一天最多说 3 句,每一句都会被认真对待。</p></div>' +
|
||||
'<div class="tm">孕妈悄悄话 · 置顶</div></div>' +
|
||||
'<div class="cm" id="cmw"><div class="cm-h" id="cmh"></div>' +
|
||||
'<div class="cm-in"><span class="me">我</span><button type="button" id="cmgo">' + PIN_PH + '</button></div>' +
|
||||
'<div id="cml"></div><div class="cm-end">— 没有更多悄悄话了 —</div></div>';
|
||||
renderComments(n); syncBar(n); loadRC(n); whRoll();
|
||||
$('#cin').value = ''; $('#cin').placeholder = PIN_PH; if (CP) CP.clear(); $('#bar').classList.remove('typing'); $('#csend').disabled = true;
|
||||
$('#dsc').scrollTop = 0; $('#dnav').classList.remove('on');
|
||||
var dt = $('#dt'); dt.classList.add('show'); dt.setAttribute('aria-hidden', 'false');
|
||||
if (push) { history.pushState({ gcNote: n.id }, '', setQuery(cur, n.id)); pushed = true; }
|
||||
else history.replaceState(history.state, '', setQuery(cur, n.id));
|
||||
}
|
||||
function hideNote() {
|
||||
var dt = $('#dt'); dt.classList.remove('show'); dt.setAttribute('aria-hidden', 'true');
|
||||
$('#cin').blur();
|
||||
@@ -2010,7 +2169,8 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
}
|
||||
var rr = e.target.closest('[data-rk]');
|
||||
if (rr) { REPLY = { k: rr.dataset.rk, n: rr.dataset.rn }; cin.placeholder = '回复 ' + REPLY.n + ':'; cin.focus(); return; }
|
||||
if (e.target.closest('#cmgo')) { REPLY = null; cin.placeholder = '说点什么…'; $('#cin').focus(); }
|
||||
if (e.target.closest('#cmgo')) { REPLY = null; cin.placeholder = (NBY[openId] || {}).pin ? PIN_PH : '说点什么…'; $('#cin').focus(); }
|
||||
if (e.target.closest('[data-star]')) { haptic(); goStar(); }
|
||||
});
|
||||
$('#blk').addEventListener('click', function () {
|
||||
var n = NBY[openId]; if (!n) return;
|
||||
@@ -2033,7 +2193,7 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
if (CP) { $('#cemo').innerHTML = MBCmPics.EMO_ICON; $('#cemo').addEventListener('mousedown', function (e) { e.preventDefault(); }); $('#cemo').addEventListener('click', function (e) { e.preventDefault(); $('#bar').classList.add('typing'); CP.toggleEmo(); }); }
|
||||
else { $('#cpic').remove(); $('#cemo').remove(); }
|
||||
cin.addEventListener('focus', function () { $('#bar').classList.add('typing'); });
|
||||
cin.addEventListener('blur', function () { setTimeout(function () { if (!canSend() && !(CP && CP.emoOpen())) { $('#bar').classList.remove('typing'); REPLY = null; cin.placeholder = '说点什么…'; } }, 120); });
|
||||
cin.addEventListener('blur', function () { setTimeout(function () { if (!canSend() && !(CP && CP.emoOpen())) { $('#bar').classList.remove('typing'); REPLY = null; cin.placeholder = (NBY[openId] || {}).pin ? PIN_PH : '说点什么…'; } }, 120); });
|
||||
cin.addEventListener('input', function () { $('#csend').disabled = !canSend(); });
|
||||
cin.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); send(); } });
|
||||
$('#csend').addEventListener('click', send);
|
||||
@@ -2052,12 +2212,12 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
send.busy = false;
|
||||
if (!j || j.code !== 200) { $('#csend').disabled = !canSend(); toast((j && j.message) || '没发出去,稍后再试'); return; }
|
||||
var rc = RC[n.id] || (RC[n.id] = { list: [], mine: [] }); rc.mine.unshift(j.result);
|
||||
var wasReply = !!REPLY; REPLY = null; cin.placeholder = '说点什么…';
|
||||
var wasReply = !!REPLY; REPLY = null; cin.placeholder = (NBY[openId] || {}).pin ? PIN_PH : '说点什么…';
|
||||
haptic();
|
||||
cin.value = ''; if (CP) CP.clear(); $('#csend').disabled = true; cin.blur(); $('#bar').classList.remove('typing');
|
||||
if (openId === n.id) { renderComments(n); syncBar(n); }
|
||||
if (!wasReply) { var w = $('#cmw'); if (w) $('#dsc').scrollTo({ top: w.offsetTop - 50, behavior: 'smooth' }); }
|
||||
toast(wasReply ? '回复已提交,审核通过后大家就能看到' : '已提交,审核通过后大家就能看到');
|
||||
toast(wasReply ? '回复已提交,审核通过后才会显示' : '已提交,审核通过后才会显示');
|
||||
}).catch(function () { send.busy = false; $('#csend').disabled = !canSend(); toast('网络不太好,稍后再试'); });
|
||||
}
|
||||
|
||||
@@ -2401,6 +2561,7 @@ html.night .gtg button.on{background:#F2725B;border-color:#F2725B;color:#fff}
|
||||
/* 11-04 微信转发图:跟着地址走 —— ?n= 笔记(g/<id>.jpg,真实投稿用广场图)、?t= 话题(g/t-<id>.jpg)、?c=ceshi 测试列表、其余广场首页;见 js/share-card.js */
|
||||
if (window.mbShareWatch) window.mbShareWatch(function () {
|
||||
var q = new URLSearchParams(location.search), n = NBY[q.get('n')], t = TBY[q.get('t')];
|
||||
if (n && n.pin) return ['../assets/share/g/home.jpg', '孕妈悄悄话|每颗星星,都是一个妈妈想对宝宝说的话']; /* 11-07 置顶笔记暂用广场首页图 */
|
||||
if (n) return n.real ? ['../assets/share/g/home.jpg', n.title] : ['../assets/share/g/' + n.id + '.jpg', n.title];
|
||||
if (t) return ['../assets/share/g/t-' + t.id + '.jpg', '#' + t.name + '#'];
|
||||
if (q.get('c') === 'ceshi') return ['../assets/share/g/ceshi.jpg', '趣味小测试|30 个孕育小测试,1 分钟测出你是哪种孕妈'];
|
||||
|
||||
11
index.html
11
index.html
@@ -9,16 +9,17 @@
|
||||
<title>兜爸心选 · 孕育陪伴</title>
|
||||
<!-- 11-04 UGC 总开关(孕妈广场 + 测一测,后台 /box_bm/plaza 顶部切换),见 js/ugc.js -->
|
||||
<script src="js/ugc.js?v=26110487"></script>
|
||||
<!-- V:26110605 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||
<!-- V:26110631 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||
<link rel="stylesheet" href="mama-box.css">
|
||||
<link rel="stylesheet" href="night.css?v=26110605">
|
||||
<link rel="stylesheet" href="night.css?v=26110608">
|
||||
<link rel="stylesheet" href="js/wh-cm.css?v=26110631">
|
||||
<!-- 11-04 用户:「顶部图片上的倒计时先删除掉」→ 不加载 hero-cd.css / hero-cd.js(文件留着,加回这两行即恢复),头图回到原样式 -->
|
||||
<!-- 夜晚模式:首屏绘制前就按上次的选择加 class,避免先白后黑闪一下(开关在首页右上角 #nightBtn,逻辑见 app.js) -->
|
||||
<script>try{if(localStorage.getItem('mmbox.night.v1')==='1'){document.documentElement.classList.add('night');document.querySelector('meta[name=theme-color]').content='#2A2340'}}catch(e){}</script>
|
||||
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
|
||||
<!-- 共享助手:授权/跳商城/位置上报(分享卡片同步,见 docs/h5-auth-standard.md 第四节) -->
|
||||
<script src="/common/wx-auth.js?v=26102601"></script>
|
||||
<script src="js/login-gate.js?v=26110605"></script>
|
||||
<script src="js/login-gate.js?v=26110618"></script>
|
||||
<!-- 11-05 用户:「消消乐的推广 banner 图,不要有外面白色的边框」→ 去掉 .game-promo 外层白底(mama-box.css 里和其他模块共用一条白底规则,这里单独盖掉;夜间同) -->
|
||||
<style>.game-promo,html.night .game-promo{background:none!important;box-shadow:none!important;backdrop-filter:none!important;-webkit-backdrop-filter:none!important}
|
||||
/* 11-05 用户:「消消乐 banner 左右要顶到两边,跟上下模块的距离再缩小几个像素,保持协调一致」→ 去掉外层内边距:左右和其他模块一样贴满(0~屏宽),上下只留模块间统一的 16px 缝(原来多出 8px);圆角跟其他模块一致 26px;同日三改:用户嫌圆角太大 → 16px */
|
||||
@@ -2475,7 +2476,7 @@ html.night .rs-pan .rp-card{background:rgba(255,255,255,.06);border-color:rgba(2
|
||||
<button class="whv-all" id="whvAll" type="button">全部</button>
|
||||
</header>
|
||||
<div class="wsk-title" id="wskTitle"></div>
|
||||
<p class="wsk-tip" id="wskTip"><i></i>点一下小星星,听听她们说了什么</p>
|
||||
<p class="wsk-tip" id="wskTip"><i></i>点一下星星,看看她说了啥</p>
|
||||
<div class="wsk-done" id="wskDone"><p>星星都落下来啦<br><span>她们的话,你都听到了</span></p><button type="button" id="wskBack">让星星回来</button></div>
|
||||
<div class="wsc" id="wsc" aria-live="polite"></div>
|
||||
<h2 class="sr-only" id="whpTitle">同孕周悄悄话</h2>
|
||||
@@ -2678,6 +2679,8 @@ html.night .rs-pan .rp-card{background:rgba(255,255,255,.06);border-color:rgba(2
|
||||
<script src="js/zodiac-art.js"></script>
|
||||
<script src="guangchang/topics.js?v=26110487"></script>
|
||||
<script src="js/ava-pool.js?v=26110487"></script> <!-- 11-05 装饰头像分配表(各模块互不重复) -->
|
||||
<script src="js/wh-themes.js?v=26110609"></script>
|
||||
<script src="js/wh-cm.js?v=26110631" defer></script>
|
||||
<script src="js/app.js"></script>
|
||||
<script src="js/kit.js?v=26110605"></script>
|
||||
<!-- 11-05 用户:孕期百问加上跟趣味小测试一样的底部 Tab(孕期百问 / 记一下 / 返回首页)。挂在 #detailQA 里,面板关着时跟着滑出屏外;
|
||||
|
||||
137
js/app.js
137
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26110605';
|
||||
var VER = '26110631';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -4453,25 +4453,10 @@
|
||||
const oid = () => hashParam('openid');
|
||||
const list = () => (data && data.list) || [];
|
||||
|
||||
/* ---------- 每周话题(2026-10-02 用户:「一周一个主题,要么大家也不知道说什么」)----------
|
||||
一条 = 从 from(北京时间那周的周一)起生效,直到下一条的 from;所以表里永远取「from ≤ 今天」的最后一条,
|
||||
没配新话题时沿用上一周的,不会空着。换话题 = 往表尾追加一条。
|
||||
后端将来若在 /whisper 返回 theme(同结构),以后端为准。
|
||||
presets 只是「不知道说什么?点一句」的示范句,发出去时不在服务端预设库里 → 照常走微信内容安全审核。 */
|
||||
const WH_THEMES = [
|
||||
/* 2026-10-02 用户:本周话题由「对宝宝说的话」改成「对老公说的话」;2026-11-01 用户:改成「对未来宝宝说的话」 */
|
||||
{ from: '2026-09-28', name: '对未来宝宝说的话',
|
||||
tip: '想对还没见面的宝宝说的,悄悄放进星空',
|
||||
say: '对未来宝宝说一句悄悄话', ph: '宝宝,妈妈想跟你说……',
|
||||
presets: ['宝宝,妈妈每天都在等你', '不用多优秀,健康快乐就好', '今天你踢了我好多下,妈妈都记着呢',
|
||||
'等你来了,带你去看海', '谢谢你选中我做你的妈妈', '我们很快就见面啦'] },
|
||||
];
|
||||
/* ---------- 每周话题:表在 js/wh-themes.js(2026-11-07 抽出,孕妈广场也要用)---------- */
|
||||
function theme() {
|
||||
if (data && data.theme && data.theme.name) return data.theme;
|
||||
const d = new Date(Date.now() + 8 * 3600e3).toISOString().slice(0, 10);
|
||||
let t = null;
|
||||
WH_THEMES.forEach(x => { if (x.from <= d) t = x; });
|
||||
return t;
|
||||
return window.MB_WH_THEME ? window.MB_WH_THEME() : null;
|
||||
}
|
||||
|
||||
/* ---------- 首页入口:一句轮播 ---------- */
|
||||
@@ -4485,8 +4470,8 @@
|
||||
}
|
||||
function paintEntry() {
|
||||
const l = list().filter(x => !x.mine);
|
||||
/* 用户:「同孕周悄悄话改成 30 周孕妈悄悄话」—— 周数跟着她自己的孕周走 */
|
||||
const nm = data && data.week ? data.week + '周孕妈悄悄话' : '孕妈悄悄话';
|
||||
/* 11-07 用户:名字去掉孕周,只叫「孕妈悄悄话」(之前是「30 周孕妈悄悄话」) */
|
||||
const nm = '孕妈悄悄话';
|
||||
$('#weName').textContent = nm;
|
||||
const th = theme();
|
||||
$('#weTopic').textContent = th ? th.name : '';
|
||||
@@ -4535,21 +4520,26 @@
|
||||
}
|
||||
/* 亮星落在夜空中上部偏右(流星往左下走,右边起步才划得够长);抖动网格,不会叠在一起 */
|
||||
function paintSky(l) {
|
||||
const n = l.length, cols = n > 16 ? 5 : 4, rows = Math.max(2, Math.ceil(n / cols));
|
||||
/* 11-07 用户:「星星要均匀地挂在星空中,不要密集地放在一个地方」→ 话题牌下面到「说一句」上面整片夜空
|
||||
(横 8–92%、竖 19–79%)切成差不多正方的格子,n 颗星按等间隔挑格子,每格里小幅抖动 */
|
||||
const n = l.length, X0 = 8, XW = 84, Y0 = 19, YH = 60;
|
||||
const pw = page.clientWidth || 390, phh = page.clientHeight || 844;
|
||||
const cols = Math.max(3, Math.round(Math.sqrt(n * (XW * pw) / (YH * phh)))), rows = Math.max(2, Math.ceil(n / cols));
|
||||
const cell = l.map((it, i) => Math.floor(i * cols * rows / n));
|
||||
const fresh = l.findIndex(x => !x.mine && !seen.has(x.id));
|
||||
const xs = l.map((it, i) => 36 + ((i % cols + .5 + ((hsh(it.id) % 100) / 100 - .5) * .7) / cols) * 58);
|
||||
const xs = l.map((it, i) => X0 + ((cell[i] % cols + .5 + ((hsh(it.id) % 100) / 100 - .5) * .6) / cols) * XW);
|
||||
/* 十三改(用户:「每点一下都是一个音符,每一个星星代表不同的音符」):
|
||||
按横向位置从左到右排成一架竖琴——越靠左越低、越靠右越高,五声音阶(C4 起),连着点就是一段旋律 */
|
||||
const rank = xs.map((x, i) => i).sort((p, q) => xs[p] - xs[q]);
|
||||
const note = [];
|
||||
rank.forEach((i, k) => { note[i] = PENTA[k % PENTA.length]; });
|
||||
$('#wskBright').innerHTML = l.map((it, i) => {
|
||||
const h = hsh(it.id), r = Math.floor(i / cols);
|
||||
const h = hsh(it.id), r = Math.floor(cell[i] / cols);
|
||||
const x = xs[i];
|
||||
const y = 21 + ((r + .5 + (((h >> 8) % 100) / 100 - .5) * .6) / rows) * 38;
|
||||
const y = Y0 + ((r + .5 + (((h >> 8) % 100) / 100 - .5) * .5) / rows) * YH;
|
||||
const sz = it.mine ? 26 : Math.round(15 + Math.min(it.hugs || 0, 30) / 30 * 11);
|
||||
return '<button type="button" class="wsk-st' + (it.mine ? ' mine' : '') + (seen.has(it.id) ? ' used' : '') + (i === fresh && !seen.size ? ' hint' : '') +
|
||||
'" data-go="' + (i + 1) + '" data-note="' + note[i].toFixed(1) + '" style="left:' + x.toFixed(1) + '%;top:' + y.toFixed(1) + '%;--s:' + sz + 'px;--tw:' + (2 + (h % 30) / 10).toFixed(1) + 's" aria-label="' + esc(it.text) + '"><i></i></button>';
|
||||
'" data-go="' + (i + 1) + '" data-hx="' + x.toFixed(1) + '" data-hy="' + y.toFixed(1) + '" data-note="' + note[i].toFixed(1) + '" style="left:' + x.toFixed(1) + '%;top:' + y.toFixed(1) + '%;--s:' + sz + 'px;--tw:' + (2 + (h % 30) / 10).toFixed(1) + 's" aria-label="' + esc(it.text) + '"><i></i></button>';
|
||||
}).join('');
|
||||
}
|
||||
function paintList(l) {
|
||||
@@ -4568,6 +4558,9 @@
|
||||
card.classList.remove('show');
|
||||
void card.offsetWidth;
|
||||
card.classList.add('show');
|
||||
/* 11-07 用户:这句话呈现出来之后,过几秒钟就让它消失 */
|
||||
clearTimeout(paintCard.t);
|
||||
paintCard.t = setTimeout(() => card.classList.remove('show'), 6500);
|
||||
const pal = it.mine ? ['#A8723A', '#E0A86A'] : AURA[(i - 1) % AURA.length];
|
||||
const au = $$('#whvAura i');
|
||||
au[0].style.backgroundColor = pal[0]; au[1].style.backgroundColor = pal[1];
|
||||
@@ -4580,6 +4573,7 @@
|
||||
m.el.remove();
|
||||
m.btn.classList.remove('flare', 'gone');
|
||||
m.btn.classList.add('used');
|
||||
reborn(m.btn);
|
||||
mts.delete(m.i);
|
||||
if (done) paintCard(m.it, m.i);
|
||||
checkDone();
|
||||
@@ -4590,8 +4584,21 @@
|
||||
· 声音用 WebAudio 现合成,不加载音频文件:点星 = 一声随机五声音阶的「叮」(连点几颗像在弹琴),
|
||||
唰出去 = 一道由低到高的风声;右上角喇叭可关,记在本地。 */
|
||||
function checkDone() {
|
||||
const all = $$('.wsk-st', page);
|
||||
$('#wskDone').classList.toggle('show', all.length > 0 && all.every(b => b.classList.contains('used')) && !mts.size);
|
||||
/* 11-07 用户:点完不要整片星空都空掉 —— 星星会再冒出来(reborn),「星星都落下来啦」不再出现 */
|
||||
$('#wskDone').classList.remove('show');
|
||||
}
|
||||
/* 11-07 用户:「星星跑掉了,过一会儿还有其他星星出来,相当于其他妈妈说的话能出来,而不是都点完之后整个星空就不能点了」
|
||||
划走的星 8~16 秒后换个位置重新亮起来(同一句话;整片夜空一直有星可点) */
|
||||
function reborn(btn) {
|
||||
setTimeout(() => {
|
||||
if (!btn.isConnected || !btn.classList.contains('used')) return;
|
||||
/* 回到自己那一格(小幅挪一点),整片星空保持均匀 */
|
||||
btn.style.left = (+btn.dataset.hx + (Math.random() - .5) * 4).toFixed(1) + '%';
|
||||
btn.style.top = (+btn.dataset.hy + (Math.random() - .5) * 3).toFixed(1) + '%';
|
||||
btn.classList.remove('used', 'cur', 'flare', 'gone');
|
||||
btn.classList.add('born');
|
||||
setTimeout(() => btn.classList.remove('born'), 1500);
|
||||
}, 8000 + Math.random() * 8000);
|
||||
}
|
||||
const SND = 'mmbox.whsnd.v1';
|
||||
let actx = null, sndOn = true;
|
||||
@@ -4701,7 +4708,7 @@
|
||||
const btn = $('.wsk-st[data-go="' + i + '"]', page);
|
||||
if (!btn) { paintCard(it, i); return; }
|
||||
btn.classList.add('cur');
|
||||
if (RM) { btn.classList.add('used'); paintCard(it, i); checkDone(); return; }
|
||||
if (RM) { btn.classList.add('used'); reborn(btn); paintCard(it, i); checkDone(); return; }
|
||||
const pr = page.getBoundingClientRect(), br = btn.getBoundingClientRect();
|
||||
const x0 = br.left + br.width / 2 - pr.left, y0 = br.top + br.height / 2 - pr.top;
|
||||
/* 瞄准左下(屏幕左边外、六成高处):越靠左起步角度越陡,保证在屏幕里拖得够长 */
|
||||
@@ -4768,6 +4775,29 @@
|
||||
mts.set(i, m);
|
||||
m.raf = requestAnimationFrame(frame);
|
||||
}
|
||||
/* 11-07 用户:「背后的一些小星星也可以点」—— 点到装饰小星附近,借一颗还没在飞的亮星挪到那儿划出去
|
||||
(话按顺序轮着来,所以背景星点不完;小星本身闪一下) */
|
||||
$('#wsk').addEventListener('click', e => {
|
||||
if (e.target.closest('.wsk-st')) return;
|
||||
const l = list(); if (!l.length) return;
|
||||
let best = null, bd = 24 * 24;
|
||||
$$('#wskStars i').forEach(d => {
|
||||
const r = d.getBoundingClientRect(), dx = r.left + r.width / 2 - e.clientX, dy = r.top + r.height / 2 - e.clientY, dd = dx * dx + dy * dy;
|
||||
if (dd < bd) { bd = dd; best = d; }
|
||||
});
|
||||
if (!best) return;
|
||||
let i = 0;
|
||||
for (let k = 1; k <= l.length; k++) { const j = (deco.n + k) % l.length; if (!mts.has(j + 1)) { i = j + 1; break; } }
|
||||
if (!i) return;
|
||||
deco.n = i - 1 + 1;
|
||||
const btn = $('.wsk-st[data-go="' + i + '"]', page); if (!btn) return;
|
||||
btn.style.left = best.style.left; btn.style.top = best.style.top;
|
||||
btn.classList.remove('used', 'born');
|
||||
best.classList.add('pk'); setTimeout(() => best.classList.remove('pk'), 900);
|
||||
haptic(); drawer(false);
|
||||
launch(i);
|
||||
});
|
||||
const deco = { n: 0 };
|
||||
function nextStar() {
|
||||
const l = list(), n = l.length;
|
||||
for (let k = 1; k <= n; k++) {
|
||||
@@ -4783,14 +4813,16 @@
|
||||
/* 2026-10-02 用户:「每个星星都是孕妈的一句话,类似这个感觉,替换掉今晚多少句悄悄话」→ 标题不再报条数 */
|
||||
/* 2026-10-02 用户:上半部分文字太占地方、挡住星星 → 标题收成一行,话题做成一行小胶囊;副标题去掉,话题说明挪进写悄悄话弹窗 */
|
||||
const th = theme();
|
||||
$('#wskTitle').innerHTML = '<div class="wsk-t">每一颗星星,都是<b>一位孕妈</b>的一句话</div>' +
|
||||
$('#wskTitle').innerHTML = '<div class="wsk-t">每颗星星,都是<b>一个妈妈</b>说的话</div>' +
|
||||
(th ? '<div class="wsk-topic"><em>本周话题</em><b>' + esc(th.name) + '</b></div>' : '<p>' + (night ? '今晚' : '今天') + ',她们和你一样在等宝宝来</p>');
|
||||
$('#whSay').firstElementChild.textContent = th && th.say ? th.say : '说一句悄悄话';
|
||||
paintDeco();
|
||||
paintSky(l);
|
||||
paintList(l);
|
||||
if (cur && list()[cur - 1]) $$('.wsk-st[data-go="' + cur + '"]', page).forEach(x => x.classList.add('cur'));
|
||||
$('#whpLeft').textContent = !data ? '' : !oid() || !data.canPost ? '填好预产期,就能说一句' : data.left ? '今天还能说 ' + data.left + ' 句' : '今天的 3 句说完啦,明天再来';
|
||||
$('#whpLeft').textContent = !data ? '' : !oid() || !data.canPost ? '填好预产期,就能说一句' :
|
||||
window.MBWhCm ? '先审核再公开,被赞 ' + MBWhCm.starLikes() + ' 次就变成星星' : /* 11-07 悄悄话第 2 步:说一句 = 发进下面的留言区 */
|
||||
data.left ? '今天还能说 ' + data.left + ' 句' : '今天的 3 句说完啦,明天再来';
|
||||
}
|
||||
function drawer(on) {
|
||||
$('#whDrawer').classList.toggle('show', on);
|
||||
@@ -4821,6 +4853,7 @@
|
||||
function closePage() {
|
||||
stopAll();
|
||||
drawer(false);
|
||||
if (fromPlaza) { fromPlaza = false; history.back(); return; } /* 从广场置顶笔记来的:回广场 */
|
||||
page.classList.remove('show');
|
||||
page.setAttribute('aria-hidden', 'true');
|
||||
}
|
||||
@@ -4837,12 +4870,13 @@
|
||||
function load(w, silent) {
|
||||
wk = w;
|
||||
if (!w) { data = null; show(false); return; }
|
||||
fetch(API + '?openid=' + encodeURIComponent(oid() || '') + '&week=' + w)
|
||||
fetch(API + '?openid=' + encodeURIComponent(oid() || '') + '&week=' + w + '&more=1') /* 11-07 用户:星星再多一些 */
|
||||
.then(r => (r.ok ? r.json() : null))
|
||||
.then(j => {
|
||||
const r = j && j.code === 200 && j.result;
|
||||
if (!r || !r.show || !(r.list || []).length) { data = null; show(false); return; }
|
||||
if (!r || !r.show || !(r.list || []).length) { data = null; show(false); autoFail(); return; }
|
||||
data = r;
|
||||
window.MB_WH_WEEK = r.week; /* 留言区「同孕周」用(js/wh-cm.js) */
|
||||
const ns = r.list.map(x => x.id + ':' + x.hugs + (x.hugged ? 'h' : '')).join('|');
|
||||
if (ns !== sig) {
|
||||
sig = ns;
|
||||
@@ -4851,8 +4885,33 @@
|
||||
if (!page.classList.contains('show')) paintPage();
|
||||
}
|
||||
show(true);
|
||||
if (autoOpen) { autoOpen = false; openPage(); }
|
||||
})
|
||||
.catch(() => { if (!silent) show(false); });
|
||||
.catch(() => { if (!silent) show(false); autoFail(); });
|
||||
}
|
||||
/* 2026-11-07 孕妈广场信息流的置顶笔记「孕妈悄悄话」点了跳到首页带 ?wh=1 → 直接打开星空页:
|
||||
· 星空页先空着盖上来(不露首页),孕周取广场也在用的 mmbox.gcwk.v1,马上拉数据,不等「此刻」;
|
||||
· 参数用完就去掉,刷新不再弹;从广场来的,关掉星空页 = 浏览器返回,回到广场;
|
||||
· 拉不到 → 提示一句再回广场。 */
|
||||
let autoOpen = /[?&]wh=1(?:&|$)/.test(location.search), fromPlaza = false;
|
||||
if (autoOpen) {
|
||||
fromPlaza = /\/guangchang\//.test(document.referrer || '') && history.length > 1;
|
||||
if (history.replaceState) {
|
||||
const q = new URLSearchParams(location.search); q.delete('wh');
|
||||
history.replaceState(history.state, '', location.pathname + (q.toString() ? '?' + q : '') + location.hash);
|
||||
}
|
||||
page.classList.add('show');
|
||||
page.setAttribute('aria-hidden', 'false');
|
||||
let gw = 0;
|
||||
try { const g = JSON.parse(localStorage.getItem('mmbox.gcwk.v1') || 'null'); gw = g && !g.pp && g.w ? +g.w : 0; } catch (e) {}
|
||||
if (gw < 4 && /[?&]wc=/.test(location.search)) gw = 20; /* 别人转发来请你点赞的:没建档也打开(孕周以服务端档案为准,没有就随便取一周的星空) */
|
||||
if (gw >= 4) setTimeout(() => load(gw), 0); else setTimeout(autoFail, 0);
|
||||
}
|
||||
function autoFail() {
|
||||
if (!autoOpen) return;
|
||||
autoOpen = false;
|
||||
toast('悄悄话暂时打不开,稍后再试');
|
||||
setTimeout(closePage, 1500);
|
||||
}
|
||||
whLoad = w => load(w, w === wk); /* 同一孕周的重拉(「此刻」每 60 秒一次)按静默刷新走:失败不收入口 */
|
||||
setInterval(() => { if (wk && !document.hidden && !busy) load(wk, true); }, 300000);
|
||||
@@ -4904,7 +4963,7 @@
|
||||
$('#whSay').addEventListener('click', () => {
|
||||
if (!data) return;
|
||||
if (!oid() || !data.canPost) { toast('填好预产期,就能和同孕周妈妈说悄悄话啦'); return; }
|
||||
if (!data.left) { toast('今天已经说了 3 句啦,明天再来'); return; }
|
||||
if (!data.left && !window.MBWhCm) { toast('今天已经说了 3 句啦,明天再来'); return; }
|
||||
ta.value = '';
|
||||
ta.maxLength = data.maxLen || 40;
|
||||
cnt();
|
||||
@@ -4914,7 +4973,8 @@
|
||||
$('#whMTopic').style.display = th ? '' : 'none';
|
||||
ta.placeholder = th && th.ph ? th.ph : '今天想对谁说点什么?';
|
||||
$('#whPresets').innerHTML = ((th && th.presets && th.presets.length) ? th.presets : (data.presets || [])).map(t => '<button class="wh-pre" type="button">' + esc(t) + '</button>').join('');
|
||||
$('#whLeft').textContent = '今天还能说 ' + data.left + ' 句';
|
||||
$('#whLeft').textContent = window.MBWhCm ? '' : '今天还能说 ' + data.left + ' 句';
|
||||
if (window.MBWhCm && MBWhCm.fillModal) MBWhCm.fillModal(); /* 11-07 弹窗重新设计(规则三步走 + 星空样式),见 js/wh-cm.js */
|
||||
openModal('modalWhisper');
|
||||
});
|
||||
$('#whPresets').addEventListener('click', e => {
|
||||
@@ -4932,6 +4992,13 @@
|
||||
const btn = $('#whSend');
|
||||
btn.disabled = true;
|
||||
btn.textContent = '正在发出…';
|
||||
/* 11-07 悄悄话第 2 步(用户定):星空页「说一句」并入留言区 —— 发成置顶笔记下的留言(先审后发),赞够门槛自动挂星 */
|
||||
if (window.MBWhCm) {
|
||||
MBWhCm.post(t, data.week)
|
||||
.then(m => { if (m) { toast(m); return; } closeModal(); toast('已提交,审核通过后才会显示'); })
|
||||
.then(() => { busy = false; btn.disabled = false; btn.textContent = '悄悄发出'; });
|
||||
return;
|
||||
}
|
||||
fetch(API + '?openid=' + encodeURIComponent(oid()), {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ text: t })
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
· 退出登录 ≠ 从没授权过:后端 user_auths.loggedOut 只在点了「退出登录」时置位,重新授权清掉;
|
||||
从没授权过的新用户不受影响。
|
||||
· 本机标记 localStorage mmbox.out.v1 = 退出时的 openid(首屏立刻知道,不等接口);
|
||||
每次进页面再和服务器(auth/profile 的 loggedOut)对一次,对不上就改标记、重载一次。
|
||||
每次进页面再和服务器(只读接口 auth/status 的 loggedOut,查不到不建记录)对一次,对不上就改标记、重载一次。
|
||||
· 退出后:首页按访客显示(app.js 里 hashParam 不给 openid);记录、评论、点赞、收藏、投稿、
|
||||
兑换、改档案这些按钮点了弹「登录后才能使用」,去登录 = 小程序授权页(没有共享库的页面回首页「我的」)。
|
||||
· 后端同样拦(box_app 返回 401 needLogin),这里只是让用户别白填。
|
||||
@@ -15,8 +15,19 @@
|
||||
var m = ((location.hash || '') + '&' + (location.search || '')).match(new RegExp('[#?&]' + n + '=([^&#?]*)'));
|
||||
return m ? decodeURIComponent(m[1]) : '';
|
||||
}
|
||||
/* 11-07(消消乐 80d347e2 实测):maternal-bridge 会把 window.localStorage 换成落库的镜像,标记写进镜像会被同步到服务端 app_states,
|
||||
刷新首屏又读不到 → 卡在登录态。退出标记只放本机真实存储:本脚本先于 bridge 加载时当场拿住;bridge 已接管时借一个同源隐藏 iframe 的。 */
|
||||
var LS = (function () {
|
||||
try {
|
||||
if (!window.MaternalBridge) return window.localStorage;
|
||||
var f = document.createElement('iframe');
|
||||
f.style.display = 'none'; f.setAttribute('aria-hidden', 'true'); f.src = 'about:blank';
|
||||
(document.body || document.documentElement).appendChild(f);
|
||||
return f.contentWindow.localStorage;
|
||||
} catch (e) { try { return window.localStorage; } catch (e2) { return null; } }
|
||||
})();
|
||||
var O = param('openid'), out = false;
|
||||
try { out = !!O && localStorage.getItem(KEY) === O; } catch (e) {}
|
||||
try { out = !!O && LS.getItem(KEY) === O; } catch (e) {}
|
||||
var root = document.documentElement;
|
||||
if (out) root.classList.add('mb-out');
|
||||
|
||||
@@ -25,7 +36,7 @@
|
||||
openid: O,
|
||||
set: function (v) {
|
||||
G.out = !!v;
|
||||
try { if (v) localStorage.setItem(KEY, O); else localStorage.removeItem(KEY); } catch (e) {}
|
||||
try { if (v) LS.setItem(KEY, O); else LS.removeItem(KEY); } catch (e) {}
|
||||
root.classList.toggle('mb-out', !!v);
|
||||
},
|
||||
login: function () {
|
||||
@@ -45,7 +56,7 @@
|
||||
|
||||
/* 和服务器对一次(在别的应用重新登录过 / 在别处退出过,本机标记会过期) */
|
||||
if (O) {
|
||||
fetch('/api/mp/public/auth/profile?openid=' + encodeURIComponent(O))
|
||||
fetch('/api/mp/public/auth/status?openid=' + encodeURIComponent(O)) /* 只读,查不到不建记录 */
|
||||
.then(function (r) { return r.ok ? r.json() : null; })
|
||||
.then(function (j) {
|
||||
if (!j || j.code !== 200 || !j.result) return;
|
||||
@@ -80,7 +91,7 @@
|
||||
box.className = 'mbg-m';
|
||||
box.setAttribute('role', 'dialog');
|
||||
box.setAttribute('aria-modal', 'true');
|
||||
box.innerHTML = '<div class="mbg-c"><h3>登录后才能使用</h3><p>你已退出登录。登录后就能记录、评论和点赞,之前的孕期资料和记录都还在。</p>' +
|
||||
box.innerHTML = '<div class="mbg-c"><h3>' + (window.MB_GATE_TITLE || '登录后才能使用') + '</h3><p>' + (window.MB_GATE_TEXT || '你已退出登录。登录后就能记录、评论和点赞,之前的孕期资料和记录都还在。') + '</p>' +
|
||||
'<div class="mbg-b"><button type="button" class="mbg-x">再看看</button><button type="button" class="mbg-ok">去登录</button></div></div>';
|
||||
box.addEventListener('click', function (e) {
|
||||
if (e.target.closest('.mbg-ok')) { hide(); G.login(); return; }
|
||||
@@ -97,25 +108,32 @@
|
||||
var REC = ['taidong', 'yunqitizhong', 'yun', 'yunqixuetang', 'gongsuopinlv', 'taierguzhong', 'chanjianrili', 'bushui']
|
||||
.map(function (t) { return '#htGrid .tg[data-tool="' + t + '"]'; }).join(',');
|
||||
var SEL = {
|
||||
home: REC + ',#vitals .vt-it,.tab-fab,.fab,.fab-w,#htEdit,.tg-add,#homeExchMod,#meExchCard,#meExchGo,#meExchOrders,#bbBanner,[data-modal="modalProfile"],[data-modal="modalWhisper"],#bfMe,.preg-card',
|
||||
home: REC + ',#vitals .vt-it,.tab-fab,.fab,.fab-w,#htEdit,.tg-add,#homeExchMod,#meExchCard,#meExchGo,#meExchOrders,#bbBanner,[data-modal="modalProfile"],[data-modal="modalWhisper"],#bfMe,.preg-card,#whSay,[data-hug],[data-wcl],[data-wrp],#wcsSend',
|
||||
plaza: '#blk,#bfv,#csend,#cpic,#cemo,#cpOk,[data-like],[data-cl],[data-rk],#cmgo,[data-write],[data-mywrite],#tabRec',
|
||||
topic: '.tc-lk,.tc-go,.tc-in .pic-b,.tc-in .emo-b'
|
||||
};
|
||||
var IN = { plaza: '#cin', topic: '.tc-in textarea' };
|
||||
var IN = { home: '#wcsIn', plaza: '#cin', topic: '.tc-in textarea' };
|
||||
function page() {
|
||||
if (document.getElementById('homeBody')) return 'home';
|
||||
if (document.getElementById('wf')) return 'plaza';
|
||||
return 'topic';
|
||||
if (/\/topic\//.test(location.pathname)) return 'topic';
|
||||
return 'other';
|
||||
}
|
||||
/* 其他应用(数胎动、体重……)引本文件时自己指定:
|
||||
window.MB_GATE_SEL = '.save-btn,.add-record'; 点了要拦的按钮(CSS 选择器)
|
||||
window.MB_GATE_IN = '.note-input'; 一聚焦就提示的输入框
|
||||
window.MB_GATE_TEXT = '登录后就能兑换好物…'; 弹窗正文(可选,标题用 MB_GATE_TITLE)
|
||||
或在自己的提交函数里:if (window.mbGate && mbGate.need()) return; */
|
||||
function selFor(map, cus) { return window[cus] || map[page()] || ''; }
|
||||
document.addEventListener('click', function (e) {
|
||||
if (!G.out || !e.target.closest) return;
|
||||
var s = SEL[page()];
|
||||
var s = selFor(SEL, 'MB_GATE_SEL');
|
||||
if (s && e.target.closest(s)) G.need(e);
|
||||
}, true);
|
||||
/* 评论输入框:一点进去就提示(别让人打完一段字才发现发不出去) */
|
||||
document.addEventListener('focusin', function (e) {
|
||||
if (!G.out || !e.target.matches) return;
|
||||
var s = IN[page()];
|
||||
var s = selFor(IN, 'MB_GATE_IN');
|
||||
if (s && e.target.matches(s)) { e.target.blur(); prompt(); }
|
||||
}, true);
|
||||
})();
|
||||
|
||||
160
js/wh-cm.css
Normal file
160
js/wh-cm.css
Normal file
@@ -0,0 +1,160 @@
|
||||
/* 星空页 · 妈妈们的留言(2026-11-07,js/wh-cm.js)
|
||||
二改:从「整页上滑盖住星空」改成半屏面板 —— 最高 68%,上面始终露出星空;深靛紫玻璃,跟星空一个色系 */
|
||||
|
||||
/* 三改(11-07):去掉「看看大家说了啥」入口 → 抽屉平时在底部露一截(--wpk 由 wh-cm.js 写),「说一句」和流星内容卡往上让 */
|
||||
.wh-page .whp-left{display:none}
|
||||
.wh-page .whp-bar{bottom:var(--wpk,58px);padding-bottom:12px}
|
||||
.wh-page .wsc{bottom:calc(var(--wpk,58px) + 104px)}
|
||||
/* 原来的「全部」抽屉不用了 */
|
||||
.wh-page .whd{display:none}
|
||||
|
||||
/* 半屏面板 */
|
||||
.wcs{position:absolute;inset:0;z-index:8;background:rgba(10,6,24,0);pointer-events:none;transition:background .3s}
|
||||
.wcs.show{pointer-events:auto;background:rgba(10,6,24,.32)}
|
||||
.wcs-p{position:absolute;left:0;right:0;bottom:0;height:58px;max-height:76%;pointer-events:auto;overflow:hidden;display:flex;flex-direction:column;border-radius:22px 22px 0 0;color:#F4ECFF;
|
||||
background:linear-gradient(#2A1F48,#1E1634);box-shadow:0 -10px 40px rgba(0,0,0,.4);transform:translateY(110%);transition:transform .35s cubic-bezier(.2,.8,.2,1),height .3s cubic-bezier(.2,.8,.2,1)}
|
||||
.wh-page.show .wcs-p{transform:none}
|
||||
.wcs-hd{flex:none;padding:8px 0 6px;touch-action:none}
|
||||
.wcs-grab{display:block;width:38px;height:4px;margin:0 auto 8px;border-radius:2px;background:rgba(255,255,255,.28)}
|
||||
.wcs-tt{display:flex;align-items:baseline;gap:8px;padding:0 16px 0 18px}
|
||||
.wcs-tt b{font-size:16px;font-weight:800;letter-spacing:.5px}
|
||||
.wcs-tt span{font-size:12px;color:rgba(232,222,255,.5)}
|
||||
.wcs-up{margin-left:auto;font-style:normal;font-size:12px;color:#FFD9B8;animation:wcmUp 1.6s ease-in-out infinite}
|
||||
@keyframes wcmUp{50%{transform:translateY(-3px)}}
|
||||
.wcs.show .wcs-up{display:none}
|
||||
.wcs:not(.show) .wcs-rule,.wcs:not(.show) .wcm-tb{opacity:0}
|
||||
.wcs-x{display:none!important}
|
||||
.wcs.show .wcs-x{display:grid!important;margin-left:auto;align-self:center;display:grid;place-items:center;width:28px;height:28px;border:0;border-radius:50%;background:rgba(255,255,255,.1);color:#fff}
|
||||
.wcm-tb{display:flex;align-items:center;gap:6px;margin:10px 18px 2px}
|
||||
.wcm-tb button{height:26px;padding:0 12px;border-radius:99px;border:0;background:rgba(255,255,255,.07);color:rgba(232,222,255,.62);font:inherit;font-size:12.5px}
|
||||
.wcm-tb button.on{background:linear-gradient(135deg,rgba(255,214,170,.92),rgba(255,170,160,.92));color:#3A2312;font-weight:800}
|
||||
/* 重点:被赞 N 次就变成星星 */
|
||||
.wcs-rule{display:flex;align-items:center;gap:8px;margin:10px 14px 0;padding:9px 12px;border-radius:14px;font-size:13px;color:#FFF3E2;
|
||||
background:linear-gradient(110deg,rgba(255,200,150,.22),rgba(150,110,255,.22));box-shadow:inset 0 0 0 1px rgba(255,214,170,.3),0 0 18px rgba(255,200,150,.12)}
|
||||
.wcs-rule svg{flex:none;width:18px;height:18px;fill:#FFD9A0;filter:drop-shadow(0 0 4px rgba(255,214,160,.9));animation:wstTw 2.2s ease-in-out infinite}
|
||||
.wcs-rule b{font-size:16px;color:#FFD08A;margin:0 1px}
|
||||
@keyframes wstTw{50%{transform:scale(.8) rotate(20deg);opacity:.7}}
|
||||
.wcs-ls{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;padding:0 12px 14px}
|
||||
.wcm-e{margin:24px 0;text-align:center;font-size:12.5px;color:rgba(232,222,255,.45)}
|
||||
.wcm-it{display:flex;gap:10px;margin-top:8px;padding:12px 10px 10px 12px;border-radius:16px;
|
||||
background:linear-gradient(140deg,rgba(255,255,255,.075),rgba(255,255,255,.03));box-shadow:inset 0 0 0 1px rgba(255,255,255,.07)}
|
||||
.wcm-av{flex:none;width:32px;height:32px;border-radius:50%;object-fit:cover;background:#3A2F5E;box-shadow:0 0 0 1.5px rgba(255,220,180,.35)}
|
||||
.wcm-bd{flex:1;min-width:0}
|
||||
.wcm-nm{display:flex;align-items:baseline;gap:6px;margin:0;font-size:12px;color:rgba(232,222,255,.45)}
|
||||
.wcm-nm b{font-size:12.5px;font-weight:700;color:#FFD9B8;max-width:9em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.wcm-tx{margin:4px 0 0;font-size:14px;line-height:1.6;color:#F2ECFA;word-break:break-word}
|
||||
.wcm-st{margin:4px 0 0;font-size:11.5px;color:#FFC07A}
|
||||
.wcm-rp{margin-top:8px;padding:7px 10px;border-radius:10px;background:rgba(255,255,255,.05)}
|
||||
.wcm-rp p{margin:0;font-size:12.5px;line-height:1.55;color:rgba(242,236,250,.78)}
|
||||
.wcm-rp p+p{margin-top:4px}
|
||||
.wcm-rp b{font-weight:700;color:#C9B8FF}
|
||||
.wcm-rp p.me b:first-child{color:#FFD9B8}
|
||||
.wcm-re{margin-top:6px;padding:2px 0;border:0;background:none;font:inherit;font-size:12px;color:rgba(232,222,255,.5)}
|
||||
.wcm-star{display:flex;width:fit-content;align-items:center;gap:4px;margin:6px 0 0;padding:2px 8px 2px 6px;border-radius:99px;font-size:11px;font-weight:700;color:#FFE2B8;
|
||||
background:linear-gradient(135deg,rgba(122,79,214,.55),rgba(90,59,170,.35));box-shadow:0 0 10px rgba(255,214,170,.25)}
|
||||
.wcm-star svg{width:10px;height:10px;fill:currentColor}
|
||||
.wcm-lk{flex:none;align-self:flex-start;display:flex;flex-direction:column;align-items:center;gap:1px;min-width:32px;padding:2px 0;border:0;background:none;font:inherit;color:rgba(232,222,255,.5)}
|
||||
.wcm-lk svg{width:18px;height:18px;fill:none;stroke:currentColor;stroke-width:1.8;transition:transform .2s}
|
||||
.wcm-lk span{font-size:11px}
|
||||
.wcm-lk.on{color:#FF8FA6}.wcm-lk.on svg{fill:currentColor;transform:scale(1.12)}
|
||||
.wcm-lk.me{opacity:.7}
|
||||
.wcm-lk.pop svg{animation:wcmPop .45s ease}
|
||||
@keyframes wcmPop{40%{transform:scale(1.45)}100%{transform:scale(1.12)}}
|
||||
.wcm-more{display:block;margin:14px auto 0;height:34px;padding:0 22px;border-radius:99px;border:1px solid rgba(255,220,180,.35);background:rgba(255,255,255,.05);color:#FFD9B8;font:inherit;font-size:13px}
|
||||
.wcm-more[hidden]{display:none}
|
||||
/* 底部输入框:发新留言 / 回复某一楼 */
|
||||
.wcs-in{flex:none;display:flex;align-items:center;gap:8px;padding:8px 12px calc(8px + env(safe-area-inset-bottom));border-top:1px solid rgba(255,255,255,.08);background:#1E1634}
|
||||
.wcs-to{flex:none;max-width:7em;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;font-size:12px;color:#FFD9B8}
|
||||
.wcs-to[hidden]{display:none}
|
||||
.wcs-in input{flex:1;min-width:0;height:36px;padding:0 14px;border:0;border-radius:99px;background:rgba(255,255,255,.09);color:#fff;font:inherit;font-size:14px;outline:none}
|
||||
.wcs-in input::placeholder{color:rgba(232,222,255,.4)}
|
||||
.wcs-in button{flex:none;height:34px;padding:0 14px;border:0;border-radius:99px;background:linear-gradient(135deg,#FFD6AA,#FFAAA0);color:#3A2312;font:inherit;font-size:13.5px;font-weight:800}
|
||||
.wcs-in button:disabled{opacity:.5}
|
||||
|
||||
/* 星星点完会再冒出来(app.js reborn) */
|
||||
.wsk-st.born i{animation:wskBorn 1.4s ease-out both}
|
||||
@keyframes wskBorn{0%{opacity:0;transform:scale(.2)}60%{opacity:1;transform:scale(1.35)}100%{opacity:1;transform:none}}
|
||||
|
||||
/* 背景小星被点到:闪一下(app.js 把一颗亮星挪过去) */
|
||||
.wsk-deco i.pk{animation:wskPk .6s ease-out}
|
||||
@keyframes wskPk{50%{transform:scale(2.4);opacity:1}}
|
||||
|
||||
/* 自己的留言:还差几个赞 + 找人帮我点赞 */
|
||||
.wcm-mf{display:flex;flex-wrap:wrap;align-items:center;gap:6px 8px;margin:8px 0 0;font-size:11.5px;color:rgba(232,222,255,.62)}
|
||||
p.wcm-mf{display:block;color:#FFC07A}
|
||||
.wcm-pg{position:relative;width:54px;height:5px;border-radius:3px;background:rgba(255,255,255,.12);overflow:hidden}
|
||||
.wcm-pg i{position:absolute;left:0;top:0;bottom:0;border-radius:3px;background:linear-gradient(90deg,#FFD6AA,#FF9FB0)}
|
||||
.wcm-share{flex-basis:100%;text-align:center;height:32px;margin-top:2px;border:0;border-radius:99px;font:inherit;font-size:13px;font-weight:800;color:#3A2312;
|
||||
background:linear-gradient(135deg,#FFE0B5,#FFAAA0);box-shadow:0 4px 16px -4px rgba(255,170,150,.6)}
|
||||
.wcm-share:active{transform:scale(.97)}
|
||||
.wcm-it.hl{animation:wcmHl 2.4s ease}
|
||||
@keyframes wcmHl{0%,60%{box-shadow:inset 0 0 0 1.5px #FFD9A0,0 0 22px rgba(255,214,160,.45)}}
|
||||
|
||||
/* 转发引导 */
|
||||
.wsh-g{position:absolute;inset:0;z-index:20;background:rgba(8,5,20,.72);animation:wshIn .25s ease}
|
||||
@keyframes wshIn{from{opacity:0}}
|
||||
.wsh-arrow{position:absolute;right:26px;top:calc(10px + env(safe-area-inset-top));width:60px;height:70px;
|
||||
background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 70' fill='none' stroke='%23FFD9A0' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M8 66C12 36 28 18 48 8' stroke-dasharray='6 6'/%3E%3Cpath d='M36 6l13 2-4 12'/%3E%3C/svg%3E") center/contain no-repeat;animation:wshA 1.2s ease-in-out infinite}
|
||||
@keyframes wshA{50%{transform:translate(4px,-4px)}}
|
||||
.wsh-c{position:absolute;left:28px;right:28px;top:120px;padding:22px 20px 18px;border-radius:22px;text-align:center;color:#F4ECFF;
|
||||
background:linear-gradient(#2E2350,#1F1738);box-shadow:inset 0 0 0 1px rgba(255,214,170,.25),0 20px 50px rgba(0,0,0,.5)}
|
||||
.wsh-c b{font-size:18px;color:#FFD9A0}
|
||||
.wsh-c p{margin:8px 0 0;font-size:13.5px;line-height:1.6;color:rgba(232,222,255,.8)}
|
||||
.wsh-c p.q{margin-top:12px;padding:10px 12px;border-radius:12px;background:rgba(255,255,255,.07);color:#fff}
|
||||
.wsh-c p.s{font-size:12.5px;color:#FFC9A0}
|
||||
.wsh-c button{margin-top:14px;height:38px;padding:0 34px;border:0;border-radius:99px;font:inherit;font-size:14px;font-weight:800;color:#3A2312;background:linear-gradient(135deg,#FFE0B5,#FFAAA0)}
|
||||
|
||||
/* 变成星星的特效:一颗星从底下升上去,炸开碎星,亮出那句话 */
|
||||
.wst-fx{position:absolute;inset:0;z-index:21;background:radial-gradient(60% 40% at 50% 34%,rgba(120,80,200,.35),rgba(8,5,20,.78));animation:wshIn .4s ease;overflow:hidden}
|
||||
.wst-fx.out{opacity:0;transition:opacity .5s}
|
||||
.wst-star,.wst-ring,.wst-sp{position:absolute;left:50%;top:30%}
|
||||
.wst-star{width:64px;height:64px;margin:-32px 0 0 -32px;
|
||||
background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath d='M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z' fill='%23FFF6E4'/%3E%3C/svg%3E") center/contain no-repeat;
|
||||
filter:drop-shadow(0 0 10px #FFD9A0) drop-shadow(0 0 26px rgba(255,200,140,.8));animation:wstUp 1.3s cubic-bezier(.3,.7,.3,1) both,wstGlow 1.8s 1.3s ease-in-out infinite}
|
||||
@keyframes wstUp{0%{transform:translateY(60vh) scale(.3);opacity:0}20%{opacity:1}80%{transform:translateY(-6px) scale(1.15)}100%{transform:none}}
|
||||
@keyframes wstGlow{50%{transform:scale(1.18) rotate(12deg)}}
|
||||
.wst-ring{width:20px;height:20px;margin:-10px 0 0 -10px;border-radius:50%;box-shadow:0 0 0 2px rgba(255,220,170,.9);opacity:0;animation:wstRing 1s 1.2s ease-out both}
|
||||
@keyframes wstRing{0%{opacity:1;transform:scale(1)}100%{opacity:0;transform:scale(12)}}
|
||||
.wst-sp{width:6px;height:6px;margin:-3px 0 0 -3px;border-radius:50%;background:#FFE7C2;box-shadow:0 0 8px #FFD9A0;opacity:0;animation:wstSp 1s ease-out both}
|
||||
@keyframes wstSp{0%{opacity:1;transform:rotate(var(--a)) translateX(0)}100%{opacity:0;transform:rotate(var(--a)) translateX(var(--d)) scale(.4)}}
|
||||
.wst-tx{position:absolute;left:30px;right:30px;top:calc(30% + 60px);text-align:center;animation:wstTx .6s 1.4s ease-out both}
|
||||
@keyframes wstTx{from{opacity:0;transform:translateY(14px)}}
|
||||
.wst-tx b{display:block;font-size:22px;font-weight:900;letter-spacing:1px;color:#FFE2B0;text-shadow:0 0 18px rgba(255,200,140,.7)}
|
||||
.wst-tx p{margin:12px 0 0;font-size:15px;line-height:1.65;color:#fff}
|
||||
.wst-tx span{display:block;margin-top:10px;font-size:12.5px;color:rgba(232,222,255,.65)}
|
||||
@media (prefers-reduced-motion:reduce){.wst-star,.wst-sp,.wst-ring,.wst-tx,.wcs-up,.wsh-arrow{animation:none!important}.wst-sp{display:none}}
|
||||
|
||||
/* 「说一句」弹窗 · 星空版(wh-cm.js fillModal 给 #modalWhisper 加 .wh-star) */
|
||||
#modalWhisper.wh-star{background:linear-gradient(170deg,#33265A 0%,#221A40 55%,#1B1533 100%);color:#F4ECFF;box-shadow:0 -14px 50px rgba(20,10,50,.6),inset 0 1px 0 rgba(255,255,255,.12);overflow-x:hidden}
|
||||
.wms-sky{display:none}
|
||||
#modalWhisper.wh-star .wms-sky{display:block;position:absolute;left:0;right:0;top:0;height:150px;pointer-events:none;opacity:.9;
|
||||
background:
|
||||
radial-gradient(1.5px 1.5px at 12% 22%,#fff,transparent),radial-gradient(1px 1px at 28% 60%,#fff,transparent),radial-gradient(1.5px 1.5px at 46% 14%,#FFE7C2,transparent),
|
||||
radial-gradient(1px 1px at 63% 40%,#fff,transparent),radial-gradient(2px 2px at 78% 18%,#FFE7C2,transparent),radial-gradient(1px 1px at 90% 52%,#fff,transparent),
|
||||
radial-gradient(1px 1px at 36% 84%,#fff,transparent),radial-gradient(1.5px 1.5px at 70% 76%,#fff,transparent),
|
||||
radial-gradient(80% 100% at 85% 0%,rgba(255,180,150,.18),transparent 70%)}
|
||||
#modalWhisper.wh-star .modal-head,#modalWhisper.wh-star>*{position:relative}
|
||||
#modalWhisper.wh-star .modal-head h3{color:#FFF1DE;font-size:21px;letter-spacing:.5px;text-shadow:0 0 16px rgba(255,200,150,.35)}
|
||||
#modalWhisper.wh-star .modal-x{background:rgba(255,255,255,.1);color:#E8DEFF}
|
||||
#modalWhisper.wh-star .wh-m-topic{margin-top:6px;color:#FFC9A0}
|
||||
#modalWhisper.wh-star .wh-m-topic span{color:rgba(232,222,255,.55)}
|
||||
#modalWhisper.wh-star .wh-m-lead{display:flex;gap:6px;margin-top:12px}
|
||||
.wms{flex:1;display:flex;align-items:center;gap:14px;padding:14px 18px;border-radius:18px;text-align:left}
|
||||
.wms.hi{font-size:16px;font-weight:800;line-height:1.45;color:#FFF3E2;
|
||||
background:linear-gradient(120deg,rgba(255,200,150,.32),rgba(150,110,255,.3));box-shadow:inset 0 0 0 1.5px rgba(255,214,170,.55),0 0 26px rgba(255,200,150,.25)}
|
||||
.wms.hi svg{flex:none;width:38px;height:38px;fill:#FFE2B0;filter:drop-shadow(0 0 6px #FFD9A0) drop-shadow(0 0 16px rgba(255,200,140,.7));animation:wstTw 2.2s ease-in-out infinite}
|
||||
.wms.hi b{font-size:24px;color:#FFD08A;margin:0 2px;text-shadow:0 0 12px rgba(255,200,140,.6)}
|
||||
#modalWhisper.wh-star .wh-ta{background:rgba(255,255,255,.07);border-color:rgba(255,255,255,.14);color:#fff}
|
||||
#modalWhisper.wh-star .wh-ta::placeholder{color:rgba(232,222,255,.4)}
|
||||
#modalWhisper.wh-star .wh-ta:focus{border-color:rgba(255,214,170,.6);box-shadow:0 0 0 3px rgba(255,214,170,.12)}
|
||||
#modalWhisper.wh-star .wh-cnt{color:rgba(232,222,255,.4)}
|
||||
#modalWhisper.wh-star .wh-m-sub{color:rgba(232,222,255,.6);font-weight:600}
|
||||
#modalWhisper.wh-star .wh-pre{background:rgba(255,255,255,.06);border-color:rgba(255,255,255,.1);color:rgba(242,236,250,.85)}
|
||||
#modalWhisper.wh-star .wh-pre.on{background:rgba(255,214,170,.18);border-color:rgba(255,214,170,.6);color:#FFE2B8}
|
||||
#modalWhisper.wh-star .dl-btn.main{background:linear-gradient(135deg,#FFE0B5,#FFAAA0);color:#3A2312;box-shadow:0 10px 26px -8px rgba(255,170,150,.7)}
|
||||
#modalWhisper.wh-star .wh-left{color:rgba(232,222,255,.5)}
|
||||
|
||||
/* 11-07 用户:「本周话题 · 对未来宝宝说的话」字大一点,和上面 23px 的标题协调(原 10.5 / 13.5px) */
|
||||
.wh-page .wsk-topic{gap:10px;margin-top:12px;padding:6px 18px 6px 6px}
|
||||
.wh-page .wsk-topic em{padding:3px 11px;font-size:13px}
|
||||
.wh-page .wsk-topic b{font-size:18px;letter-spacing:1px}
|
||||
400
js/wh-cm.js
Normal file
400
js/wh-cm.js
Normal file
@@ -0,0 +1,400 @@
|
||||
/* 星空页 · 妈妈们的留言(2026-11-07)
|
||||
· 二改(用户:「往下滑评论就挡住整个背景,不好;改成点『全部』那种半弹窗,最多拉到 2/3,一定留出一块星空;
|
||||
首页要有个明显入口,别只放右上角『全部』」):
|
||||
「说一句」按钮下面一条入口 → 半屏面板(先开到半屏,拖把手 / 往上划能拉到 2/3,再往上就不动了;往下拖关掉)。
|
||||
面板里自己滚动看全部留言。右上角「全部」也打开这个面板(原来那个只列悄悄话的抽屉不用了)。
|
||||
· 数据:广场置顶笔记 wh-<话题 from> 的评论 = cm.json 模拟(AGENTS.md 12c,不得挂星)+ /plaza/comments 已审核通过的真实评论
|
||||
+ 我自己发的(11-07 起只显示已通过的)。打开星空页才拉 cm.json(版本号跟广场 index.html 引的一致,共用缓存)。
|
||||
· 真实留言的赞走服务端(POST /plaza/comment/like,每人每条一次、不能赞自己),赞够 starLikes(后台可改,先 3)自动挂星;
|
||||
模拟留言的赞只记本机 mmbox.whcm.v1。最热 / 最新 / 同孕周 三种看法。
|
||||
· 三改(用户:「我不能给别人的留言回复」):点留言上的「回复」→ 底部输入框回复那一楼(先审后发);
|
||||
底部输入框不点「回复」时就是发一条新留言。楼的 key 同广场:模拟 = wh-xxx#序号,真实 = r:<id>。
|
||||
· 星空页「说一句」发的话也进这里(window.MBWhCm.post,app.js 调)。
|
||||
· 四改(用户 11-07):去掉「看看大家说了啥」那条入口 → 抽屉平时在屏幕底部露一截(把手 + 标题),一滑就拉开,最高再往上一点(76%);
|
||||
「被赞 3 次就变成星星」做重点;自己的留言能转发出去请人帮点赞(宿主转发卡,链接 ?wh=1&wc=<评论id>,对方打开直接定位到这条);
|
||||
自己的话变成星星时放一段特效(每条只放一次,记在 mmbox.whstar.v1);说一句的弹窗换成星空样式(fillModal)。 */
|
||||
(function () {
|
||||
var page = document.getElementById('whPage'), bar = page && page.querySelector('.whp-bar');
|
||||
if (!page || !bar || !window.MB_WH_THEME) return;
|
||||
var TH = MB_WH_THEME(), PID = 'wh-' + TH.from.replace(/-/g, '');
|
||||
var esc = function (s) { return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) { return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; }); };
|
||||
var m = ((location.hash || '') + '&').match(/[#&]openid=([^&]*)/), OID = m ? decodeURIComponent(m[1]) : '';
|
||||
var HEART = '<svg viewBox="0 0 24 24"><path d="M12 20.5s-7.5-4.6-7.5-10.2A4.3 4.3 0 0112 7.6a4.3 4.3 0 017.5 2.7c0 5.6-7.5 10.2-7.5 10.2z"/></svg>';
|
||||
|
||||
var WC = (location.search.match(/[?&]wc=([A-Za-z0-9]{1,40})/) || [])[1] || ''; /* 别人转发来请我点赞的那条 */
|
||||
|
||||
/* 半屏面板 */
|
||||
var sh = document.createElement('div');
|
||||
sh.className = 'wcs'; sh.id = 'wcs'; sh.setAttribute('aria-hidden', 'true');
|
||||
sh.innerHTML = '<div class="wcs-p" role="dialog" aria-label="妈妈们的留言">' +
|
||||
'<div class="wcs-hd"><i class="wcs-grab" aria-hidden="true"></i><div class="wcs-tt"><b>妈妈们的留言</b><span id="wcmN"></span><i class="wcs-up">上滑看看</i>' +
|
||||
'<button type="button" class="wcs-x" aria-label="收起"><svg viewBox="0 0 24 24" width="14" height="14"><path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg></button></div>' +
|
||||
'<div class="wcs-rule"><svg viewBox="0 0 20 20"><path d="M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z"/></svg><span>留言被赞 <b id="wcmRuleN">6</b> 次,就会变成一颗星星挂上去</span></div>' +
|
||||
'<div class="wcm-tb" role="tablist"><button type="button" class="on" data-wso="hot">最热</button><button type="button" data-wso="new">最新</button><button type="button" data-wso="wk">同孕周</button></div></div>' +
|
||||
'<div class="wcs-ls" id="wcsLs"><div id="wcmLs"><p class="wcm-e">留言加载中…</p></div><button type="button" class="wcm-more" id="wcmMore" hidden>再看看</button></div>' +
|
||||
'<div class="wcs-in"><span class="wcs-to" id="wcsTo" hidden></span><input id="wcsIn" type="text" maxlength="200" enterkeyhint="send" placeholder="说点什么…"><button type="button" id="wcsSend">发送</button></div>' +
|
||||
'</div>';
|
||||
page.appendChild(sh);
|
||||
var P = sh.querySelector('.wcs-p'), lsBox = sh.querySelector('#wcsLs'), ls = sh.querySelector('#wcmLs'), more = sh.querySelector('#wcmMore');
|
||||
var inp = sh.querySelector('#wcsIn'), toEl = sh.querySelector('#wcsTo'), sendB = sh.querySelector('#wcsSend');
|
||||
|
||||
var LK = 'mmbox.whcm.v1', LIKE = {};
|
||||
try { LIKE = JSON.parse(localStorage.getItem(LK) || '{}') || {}; } catch (e) {}
|
||||
var avP = function (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'; };
|
||||
var fmt = function (n) { return n >= 10000 ? (n / 10000).toFixed(1) + 'w' : String(n); };
|
||||
var ago = function (ts) {
|
||||
var d = (Date.now() - ts) / 1000;
|
||||
if (d < 3600) return Math.max(1, Math.round(d / 60)) + ' 分钟前';
|
||||
if (d < 86400) return Math.round(d / 3600) + ' 小时前';
|
||||
var t = new Date(ts + 8 * 3600e3); return (t.getUTCMonth() + 1) + '-' + t.getUTCDate();
|
||||
};
|
||||
var tT;
|
||||
function toast(t) { var x = document.getElementById('toast'); if (!x) return; x.textContent = t; x.classList.add('show'); clearTimeout(tT); tT = setTimeout(function () { x.classList.remove('show'); }, 1800); }
|
||||
|
||||
/* ---------- 数据 ---------- */
|
||||
var SIM = null, REAL = null, RREP = [], ALL = [], shown = 0, loading = false, SORT = 'hot', STAR_N = 6; // 11-07 用户改门槛为 6(以服务端 starLikes 为准)
|
||||
var top = function (c) { return !c.parent; };
|
||||
function realOf(c) {
|
||||
return { key: 'r:' + c.id, id: c.id, real: 1, n: c.nickname || '孕妈', av: c.avatar || 'assets/img/mom/k01.jpg', s: c.stage, t: c.text,
|
||||
likes: c.likes || 0, liked: !!c.liked, star: !!c.star, ts: c.at, r: [] };
|
||||
}
|
||||
function repOf(c, me) { return { p: c.parent, n: me ? '我' : (c.nickname || '孕妈'), me: me, to: c.parentName, t: c.text, ts: c.at, st: me ? c.status : '' }; }
|
||||
function load() {
|
||||
if (loading || SIM) return; loading = true;
|
||||
fetch('guangchang/cm.json?v=26110631').then(function (r) { return r.json(); }).then(function (j) {
|
||||
var PP = j.p;
|
||||
SIM = (j.c[PID] || []).map(function (c, k) {
|
||||
var p = PP[c[0]];
|
||||
return { key: PID + '#' + k, n: p[0], av: avP(p[1]), s: p[2], t: c[1], likes: c[2], st0: !!c[4],
|
||||
r: c[3].map(function (x) { var q = x[0] < 0 ? null : PP[x[0]]; return { n: q ? q[0] : '孕妈悄悄话', t: x[1] }; }) };
|
||||
});
|
||||
build(); paintGo(); focusWc();
|
||||
}).catch(function () { SIM = []; build(); focusWc(); });
|
||||
fetchReal();
|
||||
}
|
||||
function fetchReal() {
|
||||
return fetch('/api/mp/public/mama-box/plaza/comments?kind=post&target=' + encodeURIComponent(PID) + (OID ? '&openid=' + encodeURIComponent(OID) : ''))
|
||||
.then(function (r) { return r.json(); }).then(function (j) {
|
||||
var R = j && j.code === 200 && j.result || {};
|
||||
if (R.starLikes) STAR_N = R.starLikes;
|
||||
/* 11-07 用户:留言、回复一定要审核通过才显示,自己也不显示「审核中」 */
|
||||
var mine = (R.mine || []).filter(function (c) { return c.status === 'approved'; });
|
||||
REAL = (R.list || []).filter(top).map(realOf).concat(mine.filter(top).map(function (c) { var o = realOf(c); o.n = '我'; o.me = 1; o.st = c.status; return o; }));
|
||||
RREP = (R.list || []).filter(function (c) { return !top(c); }).map(function (c) { return repOf(c, 0); })
|
||||
.concat(mine.filter(function (c) { return !top(c); }).map(function (c) { return repOf(c, 1); }));
|
||||
build(); paintGo();
|
||||
celebrate(); focusWc();
|
||||
}).catch(function () { if (!REAL) { REAL = []; build(); } });
|
||||
}
|
||||
/* ---------- 我的话变成星星:特效(每条只放一次) ---------- */
|
||||
var SK = 'mmbox.whstar.v1';
|
||||
function celebrate() {
|
||||
var done = {}; try { done = JSON.parse(localStorage.getItem(SK) || '{}') || {}; } catch (e) {}
|
||||
var c = (REAL || []).filter(function (x) { return x.me && x.star && !done[x.id]; })[0];
|
||||
if (!c || celebrate.on) return;
|
||||
/* 等星空里的星星画出来再放(拉星星要几秒),放了才记下,没放成下次进来还会放 */
|
||||
celebrate.on = 1; var n = 0;
|
||||
(function wait() {
|
||||
if (++n > 40) { celebrate.on = 0; return; }
|
||||
if (!page.classList.contains('show') || !page.querySelector('.wsk-st')) { setTimeout(wait, 500); return; }
|
||||
setTimeout(function () {
|
||||
celebrate.on = 0;
|
||||
if (!page.classList.contains('show')) return;
|
||||
done[c.id] = 1; try { localStorage.setItem(SK, JSON.stringify(done)); } catch (e) {}
|
||||
starFx(c.t, true);
|
||||
}, 1200);
|
||||
})();
|
||||
}
|
||||
function starFx(text, mine) {
|
||||
snap('closed');
|
||||
var fx = document.createElement('div');
|
||||
fx.className = 'wst-fx';
|
||||
fx.innerHTML = '<i class="wst-ring"></i><i class="wst-star"></i>' + Array.apply(null, Array(14)).map(function (_, k) {
|
||||
return '<i class="wst-sp" style="--a:' + (k * 360 / 14) + 'deg;--d:' + (60 + Math.random() * 70).toFixed(0) + 'px;animation-delay:' + (1.25 + Math.random() * .2).toFixed(2) + 's"></i>'; }).join('') +
|
||||
'<div class="wst-tx"><b>' + (mine ? '你的话变成星星啦' : '这句变成星星啦') + '</b><p>“' + esc(text.length > 40 ? text.slice(0, 39) + '…' : text) + '”</p><span>' +
|
||||
(mine ? '同孕周的妈妈点开星空,就能看到你对宝宝说的话' : '谢谢你帮她点亮') + '</span></div>';
|
||||
page.appendChild(fx);
|
||||
if (navigator.vibrate) navigator.vibrate([10, 60, 20]);
|
||||
fx.addEventListener('click', function () { fx.classList.add('out'); setTimeout(function () { fx.remove(); }, 500); });
|
||||
setTimeout(function () { if (fx.isConnected) { fx.classList.add('out'); setTimeout(function () { fx.remove(); }, 500); } }, 5200);
|
||||
}
|
||||
/* ---------- 转发求赞 ---------- */
|
||||
function shareMine(id) {
|
||||
var c = (REAL || []).filter(function (x) { return x.id === id; })[0]; if (!c) return;
|
||||
var href = location.origin + location.pathname + '?wh=1&wc=' + encodeURIComponent(id);
|
||||
var title = '帮我点个赞吧!被赞 ' + STAR_N + ' 次,我对宝宝说的话就能变成一颗星星';
|
||||
var img = new URL('assets/share/g/home.jpg', document.baseURI).href;
|
||||
if (window.wxAuth && wxAuth.reportLocation) wxAuth.reportLocation({ href: href, title: title, imageUrl: img });
|
||||
var g = document.createElement('div');
|
||||
g.className = 'wsh-g';
|
||||
g.innerHTML = '<i class="wsh-arrow"></i><div class="wsh-c"><b>点右上角「···」</b><p>选「发送给朋友」或发到群里,<br>请她们帮你点个赞</p><p class="q">“' + esc(c.t.length > 30 ? c.t.slice(0, 29) + '…' : c.t) + '”</p>' +
|
||||
'<p class="s">还差 ' + Math.max(0, STAR_N - c.likes) + ' 个赞,就变成星星了</p><button type="button">知道了</button></div>';
|
||||
page.appendChild(g);
|
||||
g.addEventListener('click', function (e) { if (e.target === g || e.target.closest('button')) g.remove(); });
|
||||
}
|
||||
/* 打开的是别人转发来的链接:拉开抽屉、把那条放最前并闪一下 */
|
||||
function focusWc() {
|
||||
if (!WC || !REAL || !SIM) return;
|
||||
var w = WC; WC = '';
|
||||
try { var q = new URLSearchParams(location.search); q.delete('wc'); history.replaceState(history.state, '', location.pathname + (q.toString() ? '?' + q : '') + location.hash); } catch (e) {}
|
||||
var c = REAL.filter(function (x) { return x.id === w; })[0];
|
||||
if (!c) { toast('这条留言还在审核,或者已经找不到了'); return; }
|
||||
REAL = [c].concat(REAL.filter(function (x) { return x !== c; }));
|
||||
c.pin = 1; build();
|
||||
setTimeout(function () {
|
||||
snap('full');
|
||||
var el = ls.querySelector('.wcm-it[data-k="' + CSS.escape(c.key) + '"]');
|
||||
if (el) { el.classList.add('hl'); lsBox.scrollTop = 0; }
|
||||
toast(c.star ? '这句已经变成星星啦' : (c.me ? '这是你的留言' : '帮她点个赞,让这句话变成星星'));
|
||||
}, 600);
|
||||
}
|
||||
var wkOf = function (c) { var x = /孕\s*(\d+)\s*周/.exec(c.s || ''); return x ? +x[1] : 0; };
|
||||
/* 11-07 方案 A:模拟留言 = cm.json 标了已变星的 + 赞数(含我本机这一赞)够 STAR_N 的 */
|
||||
function simStar(c) { c.star = c.st0 || likesOf(c) >= STAR_N; }
|
||||
var likesOf = function (c) { return c.real ? c.likes : c.likes + (LIKE[c.key] ? 1 : 0); };
|
||||
var likedOf = function (c) { return c.real ? c.liked : !!LIKE[c.key]; };
|
||||
/* 最热:挂了星的在前,再按赞;最新:真实留言按时间,模拟的接在后面;同孕周:和我差 ±1 周的,按最热。我自己发的永远在最上面 */
|
||||
function sorted() {
|
||||
var pin = REAL.filter(function (c) { return c.pin; });
|
||||
var mine = REAL.filter(function (c) { return c.me && !c.pin; }), rest = REAL.filter(function (c) { return !c.me && !c.pin; }).concat(SIM);
|
||||
var hot = function (a, b) { return (!!b.star - !!a.star) || (likesOf(b) - likesOf(a)); };
|
||||
if (SORT === 'new') rest = rest.filter(function (c) { return c.real; }).sort(function (a, b) { return b.ts - a.ts; }).concat(SIM);
|
||||
else if (SORT === 'wk') {
|
||||
var w = +window.MB_WH_WEEK || 0;
|
||||
rest = w ? rest.filter(function (c) { var k = wkOf(c); return k && Math.abs(k - w) <= 1; }) : [];
|
||||
rest.sort(hot);
|
||||
} else rest.sort(hot);
|
||||
return pin.concat(mine.sort(function (a, b) { return b.ts - a.ts; }), rest);
|
||||
}
|
||||
function total() { return (REAL ? REAL.length : 0) + (SIM ? SIM.length : 0); }
|
||||
function paintGo() {
|
||||
document.getElementById('wcmN').textContent = total() ? total() + ' 条' : '';
|
||||
document.getElementById('wcmRuleN').textContent = STAR_N;
|
||||
}
|
||||
function build() {
|
||||
if (!SIM || !REAL) return;
|
||||
SIM.forEach(simStar);
|
||||
ALL = sorted(); shown = 0; ls.innerHTML = '';
|
||||
paintGo();
|
||||
if (!ALL.length) {
|
||||
ls.innerHTML = '<p class="wcm-e">' + (SORT === 'wk' ? (window.MB_WH_WEEK ? '同孕周的妈妈还没留言,你先说一句吧' : '填好预产期,就能看同孕周妈妈说了啥') : '还没有人留言') + '</p>';
|
||||
more.hidden = true; return;
|
||||
}
|
||||
add(20);
|
||||
}
|
||||
function repsOf(c) {
|
||||
var x = c.r.slice();
|
||||
RREP.forEach(function (r) { if (r.p === c.key) x.push(r); });
|
||||
return x;
|
||||
}
|
||||
function item(c) {
|
||||
var on = likedOf(c), rp = repsOf(c);
|
||||
return '<div class="wcm-it" data-k="' + esc(c.key) + '"><img class="wcm-av" src="' + esc(c.av) + '" alt="" loading="lazy" onerror="this.style.visibility=\'hidden\'">' +
|
||||
'<div class="wcm-bd"><p class="wcm-nm"><b>' + esc(c.n) + '</b>' + (c.s ? '<span>' + esc(c.s) + '</span>' : '') + (c.ts ? '<span>' + ago(c.ts) + '</span>' : '') + '</p>' +
|
||||
'<p class="wcm-tx">' + esc(c.t) + '</p>' +
|
||||
(c.star ? '<p class="wcm-star"><svg viewBox="0 0 20 20"><path d="M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z"/></svg>已经变成星星了</p>' : '') +
|
||||
(rp.length ? '<div class="wcm-rp">' + rp.map(function (x) {
|
||||
return '<p' + (x.me ? ' class="me"' : '') + '><b>' + esc(x.n) + '</b>' + (x.to && x.to !== c.n ? ' 回复 <b>' + esc(x.to) + '</b>' : '') + ':' + esc(x.t) + '</p>';
|
||||
}).join('') + '</div>' : '') +
|
||||
(c.me ? mineFoot(c) : '<button type="button" class="wcm-re" data-wrp="' + esc(c.key) + '">回复</button>') + '</div>' +
|
||||
(c.me ? '<span class="wcm-lk me">' + HEART + '<span>' + fmt(likesOf(c)) + '</span></span></div>' :
|
||||
'<button type="button" class="wcm-lk' + (on ? ' on' : '') + '" data-wcl="' + esc(c.key) + '" aria-label="赞">' + HEART + '<span>' + fmt(likesOf(c)) + '</span></button></div>');
|
||||
}
|
||||
/* 自己的留言底下:还差几个赞 + 找人帮我点赞(审核通过才能转) */
|
||||
function mineFoot(c) {
|
||||
if (c.star) return '';
|
||||
if (c.st !== 'approved') return '<p class="wcm-mf">审核通过后,就能转给朋友帮你点赞</p>';
|
||||
var n = Math.min(c.likes, STAR_N);
|
||||
return '<div class="wcm-mf"><span class="wcm-pg"><i style="width:' + Math.round(n / STAR_N * 100) + '%"></i></span><span>' + n + '/' + STAR_N + ' 个赞,还差 ' + (STAR_N - n) + ' 个就变成星星</span>' +
|
||||
'<button type="button" class="wcm-share" data-wsh="' + esc(c.id) + '">找人帮我点赞</button></div>';
|
||||
}
|
||||
function add(k) {
|
||||
ls.insertAdjacentHTML('beforeend', ALL.slice(shown, shown + k).map(item).join(''));
|
||||
shown = Math.min(ALL.length, shown + k); more.hidden = shown >= ALL.length;
|
||||
}
|
||||
function rerender(c) { var el = ls.querySelector('.wcm-it[data-k="' + CSS.escape(c.key) + '"]'); if (el) el.outerHTML = item(c); }
|
||||
more.addEventListener('click', function () { add(30); });
|
||||
|
||||
/* ---------- 点赞 / 回复 ---------- */
|
||||
function paintLk(b, c) { var on = likedOf(c); b.classList.toggle('on', on); b.querySelector('span').textContent = fmt(likesOf(c)); }
|
||||
var REPLY = null;
|
||||
function setReply(c) {
|
||||
REPLY = c;
|
||||
toEl.hidden = !c; toEl.textContent = c ? '回复 ' + c.n : '';
|
||||
inp.placeholder = c ? '回复 ' + c.n + '…' : '说点什么…';
|
||||
}
|
||||
ls.addEventListener('click', function (e) {
|
||||
var sb = e.target.closest('[data-wsh]');
|
||||
if (sb) { shareMine(sb.dataset.wsh); return; }
|
||||
var r = e.target.closest('[data-wrp]');
|
||||
if (r) {
|
||||
var rc = ALL.filter(function (x) { return x.key === r.dataset.wrp; })[0]; if (!rc) return;
|
||||
setReply(rc); snap('full'); inp.focus(); return;
|
||||
}
|
||||
var b = e.target.closest('[data-wcl]'); if (!b) return;
|
||||
var k = b.dataset.wcl, c = ALL.filter(function (x) { return x.key === k; })[0]; if (!c) return;
|
||||
if (!c.real) {
|
||||
/* 跟真实留言一样:赞了就不能取消;这一赞让它够了 STAR_N → 变星特效 */
|
||||
if (LIKE[k]) { toast('你已经赞过啦'); return; }
|
||||
LIKE[k] = 1;
|
||||
try { localStorage.setItem(LK, JSON.stringify(LIKE)); } catch (e2) {}
|
||||
var was0 = c.star; simStar(c);
|
||||
paintLk(b, c); b.classList.add('pop'); setTimeout(function () { b.classList.remove('pop'); }, 500);
|
||||
if (navigator.vibrate) navigator.vibrate(8);
|
||||
if (c.star && !was0) { rerender(c); starFx(c.t, false); }
|
||||
return;
|
||||
}
|
||||
/* 真实留言:服务端计数,赞了就不能取消(同星星的抱抱) */
|
||||
if (!OID) { toast('登录后就能点赞啦'); return; }
|
||||
if (c.liked) { toast('你已经赞过啦'); return; }
|
||||
c.liked = true; c.likes++; paintLk(b, c); b.classList.add('pop'); setTimeout(function () { b.classList.remove('pop'); }, 500);
|
||||
if (navigator.vibrate) navigator.vibrate(8);
|
||||
fetch('/api/mp/public/mama-box/plaza/comment/like?openid=' + encodeURIComponent(OID) + '&id=' + encodeURIComponent(c.id), { method: 'POST' })
|
||||
.then(function (x) { return x.json(); }).then(function (j) {
|
||||
if (!j || j.code !== 200) throw j;
|
||||
c.likes = j.result.likes; var was = c.star; c.star = !!j.result.star; paintLk(b, c);
|
||||
if (c.star && !was) { rerender(c); starFx(c.t, false); }
|
||||
}).catch(function (j) { c.liked = false; c.likes--; paintLk(b, c); toast((j && j.message) || '网络不太好,再点一次试试'); });
|
||||
});
|
||||
sh.querySelector('.wcm-tb').addEventListener('click', function (e) {
|
||||
var b = e.target.closest('[data-wso]'); if (!b || b.dataset.wso === SORT) return;
|
||||
SORT = b.dataset.wso;
|
||||
[].forEach.call(this.querySelectorAll('[data-wso]'), function (x) { x.classList.toggle('on', x === b); });
|
||||
build(); lsBox.scrollTop = 0;
|
||||
});
|
||||
|
||||
function post(text, week, reply) {
|
||||
if (!OID) return Promise.resolve('登录后就能留言啦');
|
||||
var body = { kind: 'post', target: PID, title: '孕妈悄悄话 · ' + TH.name, text: text, stage: week ? '孕 ' + week + ' 周' : '' };
|
||||
if (reply) { body.parent = reply.key; body.parentName = reply.n; }
|
||||
return fetch('/api/mp/public/mama-box/plaza/comment?openid=' + encodeURIComponent(OID), {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body)
|
||||
}).then(function (r) { return r.json(); }).then(function (j) {
|
||||
if (!j || j.code !== 200 || !j.result) return (j && j.message) || '没发出去,再试一次';
|
||||
if (j.result.status !== 'approved') return null; /* 审核中的不放进列表,通过后才显示 */
|
||||
if (reply) { RREP.push(repOf(j.result, 1)); rerender(reply); }
|
||||
else {
|
||||
var o = realOf(j.result); o.n = '我'; o.me = 1; o.st = j.result.status;
|
||||
REAL = [o].concat(REAL || []);
|
||||
if (SIM) { build(); lsBox.scrollTop = 0; }
|
||||
paintGo();
|
||||
}
|
||||
return null;
|
||||
}).catch(function () { return '网络不太好,再试一次'; });
|
||||
}
|
||||
var busy = false;
|
||||
function send() {
|
||||
var t = inp.value.trim();
|
||||
if (t.length < 2) { toast('多写一两个字吧'); return; }
|
||||
if (busy) return; busy = true; sendB.disabled = true;
|
||||
var REPLY0 = REPLY;
|
||||
post(t, +window.MB_WH_WEEK || 0, REPLY).then(function (msg) {
|
||||
busy = false; sendB.disabled = false;
|
||||
if (msg) { toast(msg); return; }
|
||||
inp.value = ''; inp.blur(); setReply(null);
|
||||
toast(REPLY0 ? '回复已提交,审核通过后才会显示' : '已提交,审核通过后才会显示');
|
||||
});
|
||||
}
|
||||
sendB.addEventListener('click', send);
|
||||
inp.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); send(); } });
|
||||
|
||||
/* ---------- 半屏面板:开 / 关 / 拖 ----------
|
||||
按高度伸缩(底部输入框一直露着):closed = 底部露一截(把手 + 标题,PEEK px),half = 55%,full = 76%(上面留出一块星空),再往上拉不动 */
|
||||
var state = 'closed', PEEK = 58;
|
||||
(function () { /* 底部露一截要加上 iPhone 底部安全区 */
|
||||
var pr = document.createElement('i'); pr.style.cssText = 'position:absolute;height:env(safe-area-inset-bottom);visibility:hidden';
|
||||
page.appendChild(pr); PEEK += pr.offsetHeight || 0; pr.remove();
|
||||
page.style.setProperty('--wpk', PEEK + 'px');
|
||||
})();
|
||||
function ph() { return page.clientHeight; }
|
||||
function hFor(s) { return s === 'closed' ? PEEK : ph() * (s === 'full' ? .76 : .55); }
|
||||
function setH(h, anim) { P.style.transition = anim ? '' : 'none'; P.style.height = Math.round(h) + 'px'; }
|
||||
function snap(s) {
|
||||
state = s;
|
||||
sh.classList.toggle('show', s !== 'closed');
|
||||
sh.setAttribute('aria-hidden', s === 'closed' ? 'true' : 'false');
|
||||
setH(hFor(s), true);
|
||||
if (s === 'closed') { inp.blur(); setReply(null); lsBox.scrollTop = 0; }
|
||||
}
|
||||
function open() { load(); snap('half'); }
|
||||
sh.querySelector('.wcs-hd').addEventListener('click', function (e) { if (state === 'closed' && !e.target.closest('.wcs-x')) open(); });
|
||||
sh.querySelector('.wcs-x').addEventListener('click', function () { snap('closed'); });
|
||||
sh.addEventListener('click', function (e) { if (e.target === sh) snap('closed'); });
|
||||
/* 右上角「全部」也开这个面板(原来那个只列悄悄话的抽屉不再用) */
|
||||
var all = document.getElementById('whvAll');
|
||||
if (all) all.addEventListener('click', function (e) { e.stopImmediatePropagation(); open(); }, true);
|
||||
/* 拖:抓把手 / 标题拖;列表在顶上时往下拖 = 拖面板;半屏时在列表上往上划 = 先把面板拉满 */
|
||||
var d0 = null;
|
||||
P.addEventListener('touchstart', function (e) {
|
||||
if (e.touches.length !== 1 || e.target.closest('.wcs-in')) return;
|
||||
d0 = { y: e.touches[0].clientY, base: P.offsetHeight, hd: !!e.target.closest('.wcs-hd'), drag: false, t: Date.now() };
|
||||
}, { passive: true });
|
||||
P.addEventListener('touchmove', function (e) {
|
||||
if (!d0) return;
|
||||
var dy = e.touches[0].clientY - d0.y;
|
||||
if (!d0.drag) {
|
||||
if (Math.abs(dy) < 6) return;
|
||||
var canDown = dy > 0 && (d0.hd || lsBox.scrollTop <= 0);
|
||||
var canUp = dy < 0 && (d0.hd || state !== 'full');
|
||||
if (!canDown && !canUp) { d0 = null; return; }
|
||||
d0.drag = true;
|
||||
}
|
||||
if (e.cancelable) e.preventDefault();
|
||||
if (state === 'closed') { load(); sh.classList.add('show'); }
|
||||
setH(Math.max(PEEK, Math.min(hFor('full'), d0.base - dy)), false);
|
||||
}, { passive: false });
|
||||
P.addEventListener('touchend', function (e) {
|
||||
if (!d0) return;
|
||||
var dd = d0; d0 = null;
|
||||
if (!dd.drag) return;
|
||||
/* 11-07 用户:「滑了一下又缩回去,要很大力气才上得去」→ 按方向判断:往上动过 24px(或甩一下)就往上一档,
|
||||
拖过半屏再往上就直接拉满;往下同理;只有几像素的小抖动才回原位 */
|
||||
var dy = e.changedTouches[0].clientY - dd.y, h = dd.base - dy, v = dy / Math.max(1, Date.now() - dd.t);
|
||||
if (dy < -24 || v < -.25) snap(state !== 'closed' || h > hFor('half') + 40 ? 'full' : 'half');
|
||||
else if (dy > 24 || v > .25) snap(state !== 'full' || h < hFor('half') - 40 ? 'closed' : 'half');
|
||||
else snap(state);
|
||||
});
|
||||
|
||||
/* 在「说一句」按钮一带往上划,也能把抽屉拉上来 */
|
||||
var by = null;
|
||||
page.addEventListener('touchstart', function (e) {
|
||||
by = state === 'closed' && !P.contains(e.target) && e.touches[0].clientY > page.getBoundingClientRect().bottom - 170 ? e.touches[0].clientY : null;
|
||||
}, { passive: true });
|
||||
page.addEventListener('touchmove', function (e) { if (by != null && by - e.touches[0].clientY > 30) { by = null; open(); } }, { passive: true });
|
||||
|
||||
/* app.js 先画了「今天还能说 N 句」(那时本脚本还没加载)—— 说一句现在发进留言区,不再按每天 3 句算,换成留言规则 */
|
||||
function fixLeft() {
|
||||
var el = document.getElementById('whpLeft');
|
||||
if (el && /^今天/.test(el.textContent)) el.textContent = '先审核再公开,被赞 ' + STAR_N + ' 次就变成星星';
|
||||
}
|
||||
new MutationObserver(fixLeft).observe(document.getElementById('whpLeft'), { childList: true, characterData: true, subtree: true });
|
||||
/* 关掉星空页时面板一起收 */
|
||||
new MutationObserver(function () {
|
||||
fixLeft();
|
||||
if (page.classList.contains('show')) { load(); setH(PEEK, false); } else snap('closed');
|
||||
}).observe(page, { attributes: true, attributeFilter: ['class'] });
|
||||
if (page.classList.contains('show')) load();
|
||||
fixLeft();
|
||||
|
||||
/* 星空页「说一句」→ 发成留言(app.js 的 #whSend 调)。返回 Promise:resolve(null) = 成功,resolve('提示') = 没发出去 */
|
||||
/* 说一句的弹窗(11-07 用户:重新设计,有点设计感;「今天还能说」这类说明放到弹窗里) */
|
||||
var M = document.getElementById('modalWhisper');
|
||||
function fillModal() {
|
||||
if (!M) return;
|
||||
M.classList.add('wh-star');
|
||||
var ld = document.getElementById('whMLead');
|
||||
/* 11-07 用户:只留「被赞 3 次变成星星」,明显一点 */
|
||||
ld.innerHTML = '<span class="wms hi"><svg viewBox="0 0 20 20"><path d="M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z"/></svg>' +
|
||||
'<span>被赞 <b>' + STAR_N + '</b> 次,<br>你的话就变成一颗星星</span></span>';
|
||||
var lf = document.getElementById('whLeft'); if (lf) lf.textContent = '审核通过后,可以转给朋友帮你点赞';
|
||||
}
|
||||
if (M && !M.querySelector('.wms-sky')) M.insertAdjacentHTML('afterbegin', '<i class="wms-sky" aria-hidden="true"></i>');
|
||||
window.MBWhCm = {
|
||||
fillModal: fillModal,
|
||||
starLikes: function () { return STAR_N; },
|
||||
post: function (text, week) {
|
||||
return post(text, week, null).then(function (msg) { if (!msg) setTimeout(function () { snap('half'); }, 400); return msg; });
|
||||
},
|
||||
open: open
|
||||
};
|
||||
})();
|
||||
17
js/wh-themes.js
Normal file
17
js/wh-themes.js
Normal file
@@ -0,0 +1,17 @@
|
||||
/* 孕妈悄悄话 · 每周话题表(2026-11-07 从 app.js 抽出来,首页和孕妈广场共用)。
|
||||
一条 = 从 from(北京时间那周的周一)起生效,直到下一条的 from;取「from ≤ 今天」的最后一条,没配新话题时沿用上一周的。
|
||||
换话题 = 往表尾追加一条。后端将来若在 /whisper 返回 theme(同结构),以后端为准。
|
||||
presets 只是「不知道说什么?点一句」的示范句,发出去照常走微信内容安全审核。 */
|
||||
window.MB_WH_THEMES = [
|
||||
/* 2026-10-02 用户:本周话题由「对宝宝说的话」改成「对老公说的话」;2026-11-01 用户:改成「对未来宝宝说的话」 */
|
||||
{ from: '2026-09-28', name: '对未来宝宝说的话', lines: ['对未来宝宝', '说的话'], /* lines:广场置顶笔记封面大字的分行 */
|
||||
tip: '想对还没见面的宝宝说的,悄悄放进星空',
|
||||
say: '对未来宝宝说一句悄悄话', ph: '宝宝,妈妈想跟你说……',
|
||||
presets: ['宝宝,妈妈每天都在等你', '不用多优秀,健康快乐就好', '今天你踢了我好多下,妈妈都记着呢',
|
||||
'等你来了,带你去看海', '谢谢你选中我做你的妈妈', '我们很快就见面啦'] },
|
||||
];
|
||||
window.MB_WH_THEME = function () {
|
||||
var d = new Date(Date.now() + 8 * 3600e3).toISOString().slice(0, 10), t = null;
|
||||
window.MB_WH_THEMES.forEach(function (x) { if (x.from <= d) t = x; });
|
||||
return t;
|
||||
};
|
||||
@@ -1102,9 +1102,6 @@ html.night .wsk-done button{color:#fff;background:rgba(255,255,255,.12);box-shad
|
||||
html.night .wmt-l::before{background:linear-gradient(90deg,#2D2730 0,rgba(45,39,48,0.7) 8%,rgba(45,39,48,0.32) 22%,rgba(35,29,45,0.22) 55%,rgba(255,255,255,.06) 90%,transparent)}
|
||||
html.night .wmt.mine .wmt-l::before{background:linear-gradient(90deg,#2F291E 0,rgba(36,32,23,0.7) 8%,rgba(36,31,23,0.32) 22%,rgba(255,210,150,.22) 55%,rgba(255,220,170,.06) 90%,transparent)}
|
||||
html.night .we-sky i{background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cdefs%3E%3CradialGradient id='g'%3E%3Cstop offset='0' stop-color='%23FFF4E6' stop-opacity='.85'/%3E%3Cstop offset='1' stop-color='%23FFF4E6' stop-opacity='0'/%3E%3C/radialGradient%3E%3C/defs%3E%3Ccircle cx='10' cy='10' r='10' fill='url(%23g)' opacity='.45'/%3E%3Cpath d='M10 .5C10.9 7.4 12.6 9.1 19.5 10 12.6 10.9 10.9 12.6 10 19.5 9.1 12.6 7.4 10.9.5 10 7.4 9.1 9.1 7.4 10 .5Z' fill='%23FFFFFF'/%3E%3C/svg%3E") center/contain no-repeat}
|
||||
html.night .we-mt{background:linear-gradient(90deg,#2D2730,rgba(45,39,48,0.5) 30%,rgba(255,255,255,0));box-shadow:0 0 6px rgba(45,39,48,0.7)}
|
||||
html.night .we-mt::before{background:#2D2730;box-shadow:0 0 6px 2px #2D2730}
|
||||
html.night .we-mt{background:linear-gradient(90deg,#2D2730,rgba(45,39,48,0.75) 30%,rgba(255,255,255,0));box-shadow:0 0 8px 1px rgba(45,39,48,0.8)}
|
||||
html.night .sk-today .sk-go{color:#fff;background:linear-gradient(135deg,#F09A80,#E07560);box-shadow:0 4px 10px rgba(224,117,96,.28)}
|
||||
html.night .modal:not(.show),html.night .sheet:not(.show){box-shadow:none!important}
|
||||
html.night .pl-feed3 .pl-row .pr-ava{box-shadow:0 0 0 1.5px #2D2730}
|
||||
|
||||
115
tools/whcm/build.py
Normal file
115
tools/whcm/build.py
Normal file
@@ -0,0 +1,115 @@
|
||||
"""孕妈悄悄话置顶笔记的 100 条模拟祝福评论(11-07 用户:「评论里按正常评论放上 100 个」)。
|
||||
lines.txt 一行一条,「||」后面是楼中楼回复(其他孕妈/宝妈)。人设从 cm.json 的 1000 人里挑:主评论用孕期人设、回复混宝妈;
|
||||
尽量挑在别的帖子下出现少的人。确定性(固定种子),重跑结果不变;只追加/覆盖 c[TARGET],不动其他帖子。"""
|
||||
import json, random
|
||||
from collections import Counter
|
||||
TARGET = 'wh-20260928'
|
||||
P = 'guangchang/cm.json'
|
||||
d = json.load(open(P, encoding='utf-8'))
|
||||
used = Counter(c[0] for k, L in d['c'].items() if k != TARGET for c in L)
|
||||
rnd = random.Random(20261107)
|
||||
import re
|
||||
def wk(p):
|
||||
m = re.match(r'孕 ?(\d+)', p[2]); return int(m.group(1)) if m else 0
|
||||
# 内容多是胎动、产检、待产(孕中晚期的事)→ 主评论只用孕 24 周以上的人设,免得「孕 6 周」说「头朝下了」
|
||||
preg = sorted([i for i, p in enumerate(d['p']) if wk(p) >= 24], key=lambda i: (used[i], i))
|
||||
anyp = sorted(range(len(d['p'])), key=lambda i: (used[i], i))
|
||||
# 11-07 用户看评论:名字套路太像(4 个「在挑名字」、4 个「今天也开心」、3 个「水獭」、虎虎的老母亲/虎虎妈妈…)→ 同一帖里前两个字、后两个字都不撞
|
||||
SIG = set()
|
||||
def fresh(i):
|
||||
n = d['p'][i][0]; ks = {'h:' + n[:2], 't:' + n[-2:]}
|
||||
return not (ks & SIG)
|
||||
def take(pool, k):
|
||||
out = []
|
||||
for i in pool:
|
||||
if len(out) >= k: break
|
||||
if fresh(i):
|
||||
n = d['p'][i][0]; SIG.update({'h:' + n[:2], 't:' + n[-2:]}); out.append(i)
|
||||
for i in pool: # 不够就只要求后两个字不撞
|
||||
if len(out) >= k: break
|
||||
n = d['p'][i][0]
|
||||
if i not in out and 't:' + n[-2:] not in SIG: SIG.add('t:' + n[-2:]); out.append(i)
|
||||
for i in pool: # 还不够就随它
|
||||
if len(out) >= k: break
|
||||
if i not in out: out.append(i)
|
||||
return out
|
||||
preg = take(preg, 160)
|
||||
mains = preg[:200]; rnd.shuffle(mains)
|
||||
anyp = [i for i in anyp if i not in set(preg) and not re.match(r'准爸爸', d['p'][i][2])] # 回复里不放「准爸爸」(还没娃,说不了打针)
|
||||
reps = take(anyp, 31); rnd.shuffle(reps)
|
||||
lines = [l.strip() for l in open('tools/whcm/lines.txt', encoding='utf-8') if l.strip()]
|
||||
assert len(lines) == 100, len(lines)
|
||||
out, ri = [], 0
|
||||
# 内容要和孕周对得上:按关键词给每条定孕周范围,从打乱后的人设里挑第一个落在范围内的
|
||||
RULES = [(r'糖耐', 24, 28), (r'预产期在月底', 37, 39), (r'过年', 25, 28),
|
||||
(r'待产包|入盆|无痛|胎位|头朝下|小床|产假|月子餐|育儿嫂|报纸|满月|儿科医生|插座|标签|绘本|包被', 32, 39),
|
||||
(r'踢的位置越来越低|孕妇照|羊水|吸奶器|数胎动|数够十下|产褥垫|剪指甲|第一批小衣服|婴儿车|疫苗时间表', 28, 39), (r'四维', 24, 30)]
|
||||
def rng(t):
|
||||
for pat, lo, hi in RULES:
|
||||
if re.search(pat, t): return lo, hi
|
||||
return 24, 39
|
||||
left = list(mains)
|
||||
def pick(t):
|
||||
lo, hi = rng(t)
|
||||
for i in left:
|
||||
if lo <= wk(d['p'][i]) <= hi: left.remove(i); return i
|
||||
raise SystemExit('没有合适人设:' + t)
|
||||
for k, l in enumerate(lines):
|
||||
parts = l.split('||')
|
||||
likes = max(3, int(520 * (0.965 ** k)) + rnd.randint(-18, 18))
|
||||
rr = []
|
||||
for t in parts[1:]:
|
||||
rr.append([reps[ri], t]); ri += 1
|
||||
out.append([pick(parts[0]), parts[0], likes, rr])
|
||||
# 11-07 用户:「评论里也加一些孕早期的孕妈,看到别人有胎动也有期待,也有自己的话对孩子说;都要真人感」
|
||||
# early.txt = 孕 6~13 周人设的留言(回复用孕中晚期/宝妈人设,像过来人);均匀插进前 100 条中间,不全堆在后面
|
||||
# early_rep.txt = 「原留言开头|早孕妈妈的回复」,挂在已有留言楼里(「看你们说胎动好羡慕」这类)
|
||||
early_p = take(sorted([i for i, p in enumerate(d['p']) if 6 <= wk(p) <= 13 and i not in set(reps) and not re.search(r'待产|宝妈|老母亲|养娃|学抱娃|拼尿布|麻麻|大宝', p[0])], key=lambda i: (used[i], i)), 51)
|
||||
er = random.Random(20261108); er.shuffle(early_p)
|
||||
ei = 0
|
||||
for t in [l.strip() for l in open('tools/whcm/early_rep.txt', encoding='utf-8') if l.strip()]:
|
||||
head, txt = t.split('|', 1)
|
||||
hit = [c for c in out if c[1].startswith(head)]
|
||||
assert len(hit) == 1, head
|
||||
hit[0][3].append([early_p[ei], txt]); ei += 1
|
||||
ERULES = [(r'两道杠|还没告诉老公|还没告诉你爸|验孕棒|小黑圈|胎心胎芽', 6, 8), (r'孕酮|查血|见了一点血', 6, 10), (r'约了NT', 10, 12),
|
||||
(r'满三个月', 8, 12), (r'才两个月', 8, 9), (r'葡萄|裤子有点紧|肚子还平', 10, 13)]
|
||||
eleft = early_p[ei:]
|
||||
def epick(t):
|
||||
lo, hi = next(((a, b) for pat, a, b in ERULES if re.search(pat, t)), (6, 13))
|
||||
for i in eleft:
|
||||
if lo <= wk(d['p'][i]) <= hi: eleft.remove(i); return i
|
||||
raise SystemExit('没有合适早孕人设:' + t)
|
||||
early = [l.strip() for l in open('tools/whcm/early.txt', encoding='utf-8') if l.strip()]
|
||||
eo = []
|
||||
for k, l in enumerate(early):
|
||||
parts = l.split('||')
|
||||
rr = []
|
||||
for t in parts[1:]:
|
||||
rr.append([reps[ri], t]); ri += 1
|
||||
eo.append([epick(parts[0]), parts[0], 0, rr])
|
||||
# 定赞数:从第 3 条起每隔约 2~3 条放一条早孕的,赞数取前后两条之间
|
||||
merged = []
|
||||
step = len(out) / len(eo)
|
||||
j = 0
|
||||
for k, c in enumerate(out):
|
||||
merged.append(c)
|
||||
while j < len(eo) and k >= 2 + j * step * .96:
|
||||
e = eo[j]; nxt = out[k + 1][2] if k + 1 < len(out) else 3
|
||||
e[2] = max(3, (c[2] + nxt) // 2 + rnd.randint(-4, 4)); merged.append(e); j += 1
|
||||
for e in eo[j:]: e[2] = 3
|
||||
# 追加在最后(模拟留言的 key 是「帖子#下标」,插在中间会让原来那 100 条下标错位,真实回复/本机点赞挂错楼);
|
||||
# 赞数已按插入位置取好,「最热」排序时自然穿插在中间
|
||||
out = out + eo
|
||||
# 11-07 用户定(方案 A):「被赞 3 次变星星」要和赞数对得上 → 赞数最高的 26 条标成已变星(第 5 位 = 1),
|
||||
# 赞数压到几十~个位(不再几百);其余 0~5 个赞(门槛 6),看着就是「还差一点」,有人帮着点一下就能变星(前端本机算)
|
||||
order = sorted(range(len(out)), key=lambda k: -out[k][2])
|
||||
NS = 26
|
||||
for r, k in enumerate(order):
|
||||
if r < NS:
|
||||
out[k][2] = max(6, int(168 * (0.88 ** r)) + rnd.randint(-5, 5)); out[k].append(1)
|
||||
else: # 11-07 用户:门槛改 6 个赞 → 没变星的 0~5 个
|
||||
out[k][2] = rnd.choice([0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 5])
|
||||
d['c'][TARGET] = out
|
||||
json.dump(d, open(P, 'w', encoding='utf-8'), ensure_ascii=False, separators=(',', ':'))
|
||||
print('ok', len(out), 'replies', ri, 'early', len(eo), 'early-replies', ei)
|
||||
39
tools/whcm/early.txt
Normal file
39
tools/whcm/early.txt
Normal file
@@ -0,0 +1,39 @@
|
||||
早上测出两道杠,妈妈在厕所蹲了十分钟没敢出来,又拆了一根测,真的是你啊||恭喜恭喜!我当时测了三根才敢信
|
||||
还没告诉你爸,打算他生日那天把验孕棒包起来送他,你先帮妈妈保密
|
||||
第一次B超你就一个小黑圈,医生说这就是你,妈妈盯着看了半天也没看懂||一模一样,我拿着单子在走廊坐了好久
|
||||
今天听到你的胎心了,一百六,跑得跟小火车似的,妈妈在诊室没忍住哭了||第一次听胎心真的会哭,我也是
|
||||
刷牙吐,闻到电梯里的香水也吐,现在出门都戴口罩
|
||||
看大家都在说胎动,好羡慕,你还是颗小豆子,妈妈啥也感觉不到,就是天天想吐||别急,到四五个月就有了,第一下像小鱼吐泡泡
|
||||
宝宝,妈妈之前胎停过一次,这次每回去医院前一晚都睡不着,你要好好的||抱抱你,我之前也胎停过,后来这个上个月生了,好好的,你也会
|
||||
妈妈开会困得直点头,领导看了好几眼,还不敢说有你了,等满三个月再讲
|
||||
你的孕酮比上周涨了,妈妈出了医院买了根烤肠奖励自己
|
||||
老家说满三个月才能往外讲,现在全世界就我和你爸知道你,憋得慌
|
||||
宝宝,妈妈以前一天两杯冰美式,现在闻到咖啡味就想吐,你是不是不喜欢咖啡
|
||||
昨晚梦到你是个圆脸小姑娘,跟你爸说了,他非说你是儿子||我怀孕的时候也老做梦,后来生出来真的跟梦里有点像
|
||||
叶酸又差点忘吃,干脆定了个闹钟
|
||||
今天去医院查血抽了五管,妈妈晕针,你爸在旁边握着我手,他手比我还凉
|
||||
听说四维能看到你的脸,妈妈还得等好几个月
|
||||
肚子还平平的,妈妈已经忍不住给你买了一双小袜子,就一双
|
||||
你来得有点突然,本来计划明年的,妈妈慌了两天,现在挺好||意外来的宝宝都是来报恩的,我家这个也是
|
||||
宝宝,妈妈吐到啥也吃不下,只有苏打饼干能咽,一周吃了四包,你记着
|
||||
约了NT,妈妈有点紧张,你乖乖的,让医生量个好数
|
||||
你爸已经开始研究婴儿车了,我说你才两个月他急啥
|
||||
妈妈在备忘录里给你写了七八个名字,划掉五个,剩下的等你爸挑
|
||||
这两天突然不怎么吐了,妈妈反而慌了,宝你要是还好好的就让我再吐一下||哈哈这心态太真实了,不吐担心吐了难受
|
||||
看大家说宝宝在肚子里打嗝伸懒腰,好想快点到那时候
|
||||
地铁上闻到韭菜包子,下车就吐了,你这鼻子比妈妈还灵
|
||||
外婆知道有你以后,每天一个电话问我吃了啥,比我上学那会儿还上心
|
||||
妈妈以前加班到半夜,有了你八点就困得睁不开眼
|
||||
你才葡萄那么大,妈妈已经在想你上幼儿园会不会哭了||当妈都这样,我现在已经在想初中选哪个学校了
|
||||
产检单上写着“宫内早孕,可见胎心胎芽”,妈妈拍照发给你爸,他回了一排感叹号
|
||||
还不敢跟同事说有你了,午饭偷偷吃话梅,被问是不是失恋了
|
||||
宝宝,妈妈现在看个广告都能哭,你爸说是激素,我说是你
|
||||
每天来这看看大家说的话,感觉没那么难熬了
|
||||
今天见了一点血,妈妈连夜去急诊,医生说你没事,回家路上哭了一路||能理解那种害怕,我也有过,后来都好好的,你也会
|
||||
你爸说等你会动了要第一个摸,外婆说她已经预约了
|
||||
手机游戏全删了,就想早点睡
|
||||
宝宝,下次B超个头长一点好不好,别再让医生说偏小
|
||||
裤子有点紧了,应该不是你,是妈妈吃多了
|
||||
还在吐,还在困,还在担心,一想到再过几个月就能感觉到你动,又觉得还行
|
||||
看大家都在收待产包了,妈妈连孕妇装都还没买,你慢慢长||不着急,日子快得很,一眨眼就到了
|
||||
你爸贴着肚子跟你说了半天话,我说你现在还没耳朵呢,他说先练着
|
||||
12
tools/whcm/early_rep.txt
Normal file
12
tools/whcm/early_rep.txt
Normal file
@@ -0,0 +1,12 @@
|
||||
你爸第一次摸到你动,手一|看得好羡慕,我才两个月,还只能隔着肚皮瞎猜
|
||||
宝宝你是在打嗝吗,一跳一|等我也能感觉到宝宝打嗝那天,一定要录下来
|
||||
四维看到你的脸了,鼻子随|好期待四维,我现在B超上还是一颗小豆子
|
||||
你啊,凌晨两点准时开踢,|看完好想快点被踢醒,现在只有被孕吐吐醒
|
||||
前三个月你把妈妈折腾掉六|我正在吐的阶段,看到能吃下一碗饭了就有盼头了
|
||||
你爸拿洋娃娃练换尿布练了|我老公也说要练,我说先把我的孕吐伺候好再说
|
||||
今天听胎心你一直躲,医生|我连胎心都是上周才第一次听到的,听完在诊室里哭了
|
||||
偷偷把你的B超照设成手机|我也设了!虽然只是个小黑圈
|
||||
给你收拾待产包,发现产褥|收藏了,等我到那时候照着买
|
||||
你的小床到了,你爸装了一|我们才三个月,已经在看小床了哈哈
|
||||
睡不着就刷别人家宝宝的视|我也是,现在每天睡前刷别人家宝宝
|
||||
医生让多走走,今天带你在|我现在连下楼都懒,只想躺着,等过了前三个月我也去走
|
||||
100
tools/whcm/lines.txt
Normal file
100
tools/whcm/lines.txt
Normal file
@@ -0,0 +1,100 @@
|
||||
你啊,凌晨两点准时开踢,妈妈直接被你踢醒,白天上班困成狗,咖啡还不让喝||同,两点准时开工,比闹钟还准
|
||||
今天产检排了三个小时队,轮到我了你倒好,一动不动,医生按了半天肚子,吓死我了,结果你在睡觉
|
||||
你爸第一次摸到你动,手一下缩回去了,说“真有人啊??”你说他是不是傻||哈哈哈我老公也是这反应
|
||||
崽 商量个事 出来以后晚上让我睡满四个小时行不行 就四个
|
||||
为了你把糖耐那瓶糖水硬灌下去了,齁得要命,抽了三次血,还好过了,你可得给我好好长
|
||||
宝宝你是不是又长大了,妈妈腰疼得翻身都要喊你爸推,跟翻咸鱼似的
|
||||
又偷吃了半个西瓜,测血糖前心虚得要死,还好没超,宝你别告诉医生||我也是管不住嘴,测完才敢松口气
|
||||
你外婆给你寄了一箱小衣服,全是她拿旧秋衣改的,说纯棉软和,我说现在都买新的,她不听。到时候先给你穿她的
|
||||
你可千万别学你爸挑食,三十多岁了还不吃胡萝卜
|
||||
今天地铁上第一次有人给咱俩让座,一个大学生姐姐,妈妈有点不好意思,还是坐了
|
||||
小家伙,你把妈妈的脚都压肿了,鞋穿不进去,你爸给买了双大一码的拖鞋,丑死了
|
||||
宝宝你是在打嗝吗,一跳一跳的,妈妈手放上去数了二十多下才停
|
||||
宝宝,妈妈已经在看你的疫苗时间表了,第一针出生就打,到时候哭了妈妈抱||第一针真的心疼,打完赶紧喂奶就好了
|
||||
这个月给你买东西花了快两千,你爸说比给他买的多十倍,嗯,他说得对
|
||||
偷偷把你的B超照设成手机屏保了,被同事看见,现在全办公室都知道你了
|
||||
医生说你偏小一周,妈妈这几天每顿多吃一个鸡蛋,你争点气,下回追上||我家也偏小,多吃牛肉,下次产检就追上了
|
||||
宝宝,妈妈怕疼,决定打无痛了,你别嫌我
|
||||
你爸拿洋娃娃练换尿布练了一个月,现在一分钟能换好,天天让我验收,你出来给他打个分
|
||||
给你收拾待产包,发现产褥垫买少了,又下单两包,你可别来太早
|
||||
前三个月你把妈妈折腾掉六斤,现在终于能吃下一碗饭了,谢谢你高抬贵手
|
||||
睡不着就刷别人家宝宝的视频,越刷越想看看你长啥样
|
||||
宝宝,奶奶说你是男孩,外婆说是女孩,俩人赌了一顿火锅,你看着办||我家也赌了,赌注是一只金镯子哈哈
|
||||
今天听胎心你一直躲,医生让我吃块巧克力,吃完你立马动了,小馋鬼
|
||||
你的小床到了,你爸装了一晚上,最后多出来两颗螺丝,妈妈没敢问
|
||||
宝宝,妈妈这两天动不动就哭,你爸问我怎么了我也说不上来,不关你的事啊
|
||||
四维看到你的脸了,鼻子随你爸,嘴巴随我,还行,不丑
|
||||
你预产期在过年那几天,红包妈妈先替你收着了(不是||红包存起来+1,我准备给宝宝开个账户
|
||||
宝宝,妈妈产假一共一百五十八天,还没开始休,已经在算能陪你多少天了
|
||||
医生让多走走,今天带你在楼下转了四十分钟,妈妈腿都酸了,你倒是睡得香
|
||||
你爸为了你戒烟五个月了,一根没抽,这个你出来得夸夸他
|
||||
最近踢的位置越来越低了,是要入盆了吗,有没有懂的姐妹
|
||||
给你洗了第一批小衣服,晾了一阳台,邻居阿姨问妈妈是不是快生了,我说还早呢
|
||||
你的名字吵了一个月了,你爸起的太土,我起的他说太文艺,要不你出来自己挑吧||我家也是吵了三个月,最后抓阄定的
|
||||
宝宝,妈妈怀你胖了十八斤,医生说正常,那我就当真了
|
||||
今天感觉你动得少,妈妈喝了口冰水,你马上踢我一脚,行,知道你在
|
||||
以后不给你报一堆兴趣班,你喜欢啥学啥,妈妈小时候被逼学琴学到吐
|
||||
半夜腿抽筋疼醒,你爸迷迷糊糊给我掰脚,掰完倒头就睡,呼噜都没停,你说这人
|
||||
今天产检前面四十多个人,妈妈揣着你站了一上午,腰要断了||产检那天真的累,记得带个小板凳
|
||||
你外公不会用微信,这两天专门学了视频通话,说等你出来天天看你
|
||||
宝宝,妈妈今天把钥匙放冰箱里了,找了半小时,你爸说是你把我脑子借走了
|
||||
妈妈发誓以后不在朋友圈天天晒你。争取吧||争取两个字太真实了哈哈
|
||||
宝宝,家里有只猫叫可乐,比你先来的,以后你俩别打架
|
||||
产检一切正常,出了医院妈妈就去吃了碗牛肉面,加俩蛋,你也吃到了吧
|
||||
这两天你动得少了点,妈妈晚饭后躺着数你,数够十下才敢睡
|
||||
你的第一套衣服是妈妈在拼多多九块九抢的,质量居然还行,别嫌弃
|
||||
你爸说等你出来教你打游戏,妈妈已经驳回了,上小学之前想都别想||驳回得好,我家那位已经在挑游戏机了
|
||||
宝宝,预产期在月底,那会儿挺冷了,包被给你买了两条,不知道够不够
|
||||
妈妈开会坐了俩小时,你在里面踢了一个半小时,对会议内容有意见是吧
|
||||
不图你多聪明,能吃能睡就行,别学妈妈天天失眠
|
||||
上班时你在里面一拱一拱的,妈妈在工位上偷偷乐,同事以为我在摸鱼看视频
|
||||
宝宝,奶奶把月子餐菜单写好了,三十天不重样,妈妈看完有点感动,又有点怕
|
||||
等你会走路了带你去楼下喂鸽子,那边的鸽子特别肥
|
||||
今天带你去拍了孕妇照,摄影师说妈妈是他最近拍过最圆的一个,都怪你||孕妇照一定要拍,以后翻出来很有意思
|
||||
你爸每晚对着肚子给你讲三国,已经讲到赤壁了,你听不听得懂不知道,我快会背了
|
||||
宝宝,等你出来,妈妈第一件事肯定是数你手指脚趾,别笑我
|
||||
今天走快了点肚子就发紧,吓得赶紧找地方坐,以后再也不赶地铁了
|
||||
以前妈妈一顿两碗饭,现在半碗就顶到胃,你挤得我好难受
|
||||
你小姨给你买了个会唱歌的摇铃,她自己先玩了一下午
|
||||
宝宝,妈妈最近特别想吃酸的,你爸念叨酸儿辣女,我说这是迷信,你是啥都行
|
||||
今天在医院看到个刚出生的宝宝,皱巴巴红通通的,你刚出来是不是也这样
|
||||
宝宝,妈妈打算喂你母乳到一岁,能不能坚持住不知道,尽量吧
|
||||
抓到你爸偷偷搜怎么给新生儿剪指甲,他说是工作需要,呵
|
||||
家里插座全给你装了保护盖,你爸说你还早着呢,妈妈说早装早安心
|
||||
你的待产包收好放门口了,妈妈每次出门都要看一眼||我直接放车后备箱了,省得到时候手忙脚乱
|
||||
生完想带你回老家住一阵,有院子,空气好,就是离医院远了点
|
||||
你今天在超市踢到妈妈膀胱,差点没憋住,一路小跑找厕所,你可真行
|
||||
你爸给你开了个奶粉钱账户,每月自动扣,他说这叫仪式感
|
||||
想买份宝宝出生那天的报纸留着,问了一圈,报亭都没了
|
||||
胎位查了,你头朝下了!宝,就这样,千万别再转了||我家当时也是臀位,做了两周膝胸卧位才转过来
|
||||
外婆说她生妈妈疼了一天一夜,听完我更坚定要打无痛了,你别怪妈妈怕疼
|
||||
你爸开始学做月子餐,鲫鱼汤第一锅糊了,第二锅还能喝,你以后可别学他做饭
|
||||
小区门口那个滑滑梯每天都排队,等你会走了咱们也去排
|
||||
又被你踢醒,一看手机三点十二,你这作息以后可咋整||三点的我也在,一起数羊
|
||||
托人打听了个儿科医生,说看病很仔细,妈妈先把号码存上了,希望用不上
|
||||
奶奶今天送来一只老母鸡,说给我补补,我懂,是给你补
|
||||
听说宝宝哭了抱着转圈就好,妈妈已经做好在客厅转一千圈的准备了||我家已经买了个摇椅,替我转
|
||||
今天试着给你读了本好饿的毛毛虫,你听没听见不知道,妈妈自己读得挺开心
|
||||
你的小名定了,叫豆豆,大名还在吵,先豆豆叫着||豆豆好可爱,我家叫团团
|
||||
妊娠纹还是长了,油涂了一罐也没用,算了随它吧||说得好,我也不涂了
|
||||
宝宝,妈妈准备请三个月育儿嫂,贵是贵,但妈妈想睡觉
|
||||
同事凑钱送了个婴儿背带,挺意外的,平时也没多熟
|
||||
你的满月酒,你爸说摆两桌,妈妈说一桌够了,还没吵出结果
|
||||
今天忘数胎动了,晚上补数了一个小时,你还挺配合
|
||||
你一天一个样,妈妈的孕妇裤都快穿不下了
|
||||
以前妈妈怕打针怕得要死,怀你打了十几针,现在完全无感了
|
||||
明年夏天带你去海边,妈妈自己都没去过几回
|
||||
医生说羊水偏少,让多喝水,今天喝了三大杯,你在里面泡舒服了没
|
||||
老公在母婴店推婴儿车推了半小时,来回试,店员都看笑了
|
||||
宝宝,生你之前妈妈想再吃一次火锅,微辣,就一次,你批准不||吃!微辣问题不大,我上周刚吃
|
||||
最近老梦到你会说话了,一开口叫妈,醒了还挺失落
|
||||
宝宝,吸奶器还没用上,妈妈教程已经看了十几个,到时候你配合点
|
||||
今天你在肚子里翻了个大身,肚子整个歪到一边,你爸说像外星人
|
||||
妈妈以前天天熬到两点,怀你以后十一点前就睡了,你出来别让我破功
|
||||
你爸在家族群发了你的B超照,全家都说像他,B超能看出个啥
|
||||
宝宝,妈妈也不知道自己能不能当好妈妈,边当边学吧,你多担待
|
||||
你的小衣服标签妈妈挨个剪了,怕扎你,剪了一个多小时
|
||||
希望你出生那天是晴天,你爸说下雨也行,反正都高兴
|
||||
以后跟你说你在肚子里有多能踢,你肯定不信
|
||||
尿蛋白有点高,要复查,妈妈有点慌,你在里面好好的就行||别太慌,我上次也是,复查就正常了
|
||||
给你攒了一箱绘本,都是闲鱼收的二手,九成新,你别嫌弃
|
||||
@@ -64,7 +64,7 @@
|
||||
/* 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=26110464'; s.onload = function () { window.mbShare('../../assets/share/t/' + SLUG + '.jpg'); }; document.head.appendChild(s); })();
|
||||
/* 11-07 退出登录后只看不写(js/login-gate.js):点赞、评论、发图都弹「登录后才能使用」 */
|
||||
if (!window.mbGate) (function () { var s = document.createElement('script'); s.src = '../../js/login-gate.js?v=26110605'; document.head.appendChild(s); })();
|
||||
if (!window.mbGate) (function () { var s = document.createElement('script'); s.src = '../../js/login-gate.js?v=26110610'; 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) {} }
|
||||
@@ -98,8 +98,8 @@
|
||||
var st = loadMe(), L = [], open = false, RC = { list: [], mine: [] }, OID = UK === '_guest' ? '' : UK;
|
||||
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, img: c.images }; }));
|
||||
/* 11-07 用户:评论、回复一定要审核通过才显示,自己也不显示「审核中」—— 只留已通过的;本机旧评论(从没审过)也不再显示 */
|
||||
return RC.mine.filter(function (c) { return c.status === 'approved'; }).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, c.images]; }); }
|
||||
var cpH = function (a) { return window.MBCmPics ? MBCmPics.html(a) : ''; }; /* 11-05 评论里的图 */
|
||||
@@ -191,7 +191,7 @@
|
||||
var wasReply = !!REPLY; setReply(null);
|
||||
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 ? '回复已提交,审核通过后大家就能看到' : '已提交,审核通过后大家就能看到');
|
||||
toast(wasReply ? '回复已提交,审核通过后才会显示' : '已提交,审核通过后才会显示');
|
||||
}).catch(function () { bt.busy = false; bt.disabled = !canSend(); toast('网络不太好,稍后再试'); });
|
||||
});
|
||||
function toast(m) {
|
||||
|
||||
Reference in New Issue
Block a user