首页此刻改2行轮播;新增孕妈广场(首页横滑话题卡+15话题瀑布流+详情点赞评论,占位内容)v26110109

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
deploy
2026-11-01 06:16:52 +00:00
parent 225fb21bf3
commit 107e02a47e
5 changed files with 3118 additions and 4 deletions

2358
guangchang/data.js Normal file

File diff suppressed because it is too large Load Diff

632
guangchang/index.html Normal file
View File

@@ -0,0 +1,632 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<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">
<title>孕妈广场</title>
<!-- 孕妈广场(2026-11-01 第一版,占位内容,纯前端;mama-box 首页「孕妈广场」横滑卡点进来)
用户原话:「点进去之后变成小红书那种瀑布流,每一个话题都是那样的。先整理出来 15 个话题,先做占位放上。点击详情页的时候可以点赞和评论。」
· 话题表 topics.js(与首页共用);笔记 / 评论池 data.js(每话题 7 条笔记 + 10 条评论池,评论按笔记 id 确定性抽 3~8 条)。
· URL:?t=话题id 进对应话题,&n=笔记id 直接打开详情;切 tab 只用 replaceState 改 query,**hash 原样保留**(身份 #openid 在里面)。
· 点赞 / 收藏 / 评论 / 评论点赞只存本机 localStorage「mmbox.gc.v1」,按 openid 分开;正式版接后端时整块替换 store。
· 返回:详情层 → history.back() 关层;列表 → 有站内历史就 history.back(),没有就回 ../(保留身份,去掉 t/n)。
· 禁止下拉回弹:固定外壳 + 容器滚动 + touchmove 拦截(同首页那段:往上找能朝该方向滚的容器,找不到才拦)。 -->
<style>
:root{
--brand:#F4593C;--ink:#2B2320;--ink-2:#6B615B;--muted:#9A8380;--line:#F1E7E1;
--bg:#F8F3EF;--peach:#FFEDE6;--btn:linear-gradient(135deg,#F77B55,#EE5A3E);
--safe-b:env(safe-area-inset-bottom);
}
*{box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{margin:0;padding:0;height:100%;overflow:hidden;overscroll-behavior:none}
body{background:var(--bg);font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Microsoft YaHei","Segoe UI",Roboto,sans-serif;
color:var(--ink);-webkit-font-smoothing:antialiased;font-size:14px;line-height:1.5;-webkit-text-size-adjust:100%}
button{font:inherit;color:inherit;border:0;background:none;padding:0;cursor:pointer}
.ic{width:16px;height:16px;flex:none;display:inline-block;vertical-align:middle}
.hide{display:none!important}
/* ---------- 外壳:唯一的滚动容器 ---------- */
#sc{position:fixed;inset:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior:none;
background:radial-gradient(220px 180px at 100% 0,rgba(255,196,214,.45),rgba(255,196,214,0) 70%),radial-gradient(240px 200px at 0 40px,rgba(255,206,170,.45),rgba(255,206,170,0) 70%),var(--bg)}
#sc::-webkit-scrollbar{display:none}
.top{position:sticky;top:0;z-index:20;padding-top:env(safe-area-inset-top);background:rgba(248,243,239,.9);-webkit-backdrop-filter:blur(14px) saturate(160%);backdrop-filter:blur(14px) saturate(160%);transition:box-shadow .2s}
.top.on{box-shadow:0 4px 14px rgba(160,110,90,.10)}
.nav{display:flex;align-items:center;gap:6px;height:46px;padding:0 10px}
.nav .bk{width:36px;height:36px;display:grid;place-items:center;border-radius:50%;color:var(--ink)}
.nav .bk:active{background:rgba(0,0,0,.05)}
.nav .bk .ic{width:22px;height:22px}
.nav h1{flex:1;margin:0;text-align:center;font-size:17px;font-weight:900;letter-spacing:.5px}
.nav h1 span{background:linear-gradient(transparent 60%,#FFD0DC 60%,#FFD0DC 92%,transparent 92%);padding:0 3px}
.nav .sp{width:36px}
.tabs{display:flex;gap:6px;padding:2px 12px 10px;overflow-x:auto;overflow-y:hidden;scroll-behavior:smooth;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}
.tabs::-webkit-scrollbar{display:none}
.tabs::after{content:"";flex:none;width:6px}
.tabs button{flex:none;height:30px;padding:0 13px;border-radius:99px;background:rgba(255,255,255,.75);border:1px solid #F0E2DA;font-size:13px;font-weight:600;color:#7A6663;white-space:nowrap;transition:background .15s,color .15s}
.tabs button.on{background:#2F2424;border-color:#2F2424;color:#fff;font-weight:800}
.tabs button .hb{display:inline-block;margin-left:3px;padding:0 4px;border-radius:5px;background:#FF5A3C;color:#fff;font-size:9.5px;font-weight:800;line-height:15px;vertical-align:1px}
.tabs button.on .hb{background:#FF7A5C}
/* ---------- 话题头卡 ---------- */
.th{position:relative;margin:4px 12px 2px;padding:16px 16px 14px;border-radius:22px;overflow:hidden;
background:linear-gradient(150deg,var(--c1),var(--c2));color:var(--tk);box-shadow:0 12px 26px -16px var(--tk)}
.th .tg{position:absolute;right:-6px;top:-10px;font-size:110px;font-weight:900;line-height:1;color:#fff;opacity:.5;font-family:"STKaiti","KaiTi","Kaiti SC",serif;pointer-events:none}
.th .tc{display:inline-flex;align-items:center;height:20px;padding:0 8px;border-radius:7px;background:rgba(255,255,255,.65);font-size:11px;font-weight:800;letter-spacing:.5px}
.th h2{position:relative;margin:8px 0 4px;font-size:21px;font-weight:900;line-height:1.3;color:#2A1E1E;letter-spacing:.3px}
.th h2 em{font-style:normal;color:var(--tk);margin-right:2px}
.th p{position:relative;margin:0;max-width:270px;font-size:12.5px;line-height:1.6;color:#5E4E4A}
.th .tf{position:relative;display:flex;align-items:center;gap:10px;margin-top:12px}
.th .ts{flex:1;display:flex;align-items:center;gap:7px;font-size:12px;color:#6E5C57}
.th .ts b{color:var(--tk);font-weight:900;font-variant-numeric:tabular-nums}
.th .avs{display:flex}
.th .avs img{width:20px;height:20px;border-radius:50%;object-fit:cover;border:1.5px solid rgba(255,255,255,.9);margin-left:-6px;background:#FFE3D3}
.th .avs img:first-child{margin-left:0}
.th .join{flex:none;height:32px;padding:0 14px;border-radius:99px;background:#fff;color:var(--tk);font-size:13px;font-weight:800;box-shadow:0 4px 10px rgba(0,0,0,.08);display:inline-flex;align-items:center;gap:4px}
.th .join .ic{width:14px;height:14px}
/* 「全部」时的一句引导 */
.intro{display:flex;align-items:center;gap:8px;margin:2px 14px 0;font-size:12px;color:var(--muted)}
.intro b{color:var(--ink);font-weight:800}
.intro .seg{margin-left:auto;display:flex;padding:2px;border-radius:99px;background:#EFE4DD}
.intro .seg button{height:24px;padding:0 10px;border-radius:99px;font-size:12px;font-weight:700;color:#8A746E}
.intro .seg button.on{background:#fff;color:#E04A2E;box-shadow:0 1px 4px rgba(190,120,96,.18)}
/* ---------- 瀑布流 ---------- */
.wf{display:flex;align-items:flex-start;gap:8px;padding:10px 12px 0}
.wf .col{flex:1;min-width:0;display:flex;flex-direction:column;gap:8px}
.nc{display:block;width:100%;text-align:left;border-radius:14px;overflow:hidden;background:#fff;box-shadow:0 3px 12px rgba(170,110,90,.08);animation:ncIn .35s ease both}
.nc:active{transform:scale(.985)}
@keyframes ncIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.nc .cvw{position:relative}
.nc .nb{padding:8px 9px 9px}
.nc h3{margin:0;font-size:13.5px;font-weight:700;line-height:1.45;color:#2A2220;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.nc .nf{display:flex;align-items:center;gap:5px;margin-top:7px}
.nc .nf img{width:18px;height:18px;border-radius:50%;object-fit:cover;flex:none;background:#FFE3D3}
.nc .nf .nm{flex:1;min-width:0;font-size:11px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.lk{flex:none;display:inline-flex;align-items:center;gap:2px;min-width:44px;justify-content:flex-end;height:22px;font-size:11.5px;color:var(--muted);font-variant-numeric:tabular-nums}
.lk .ic{width:15px;height:15px}
.lk.on{color:#F0453A}
.lk.pop .ic{animation:pop .35s ease}
@keyframes pop{40%{transform:scale(1.35)}}
.nc .ttag{position:absolute;left:7px;top:7px;z-index:2;height:18px;padding:0 6px;border-radius:6px;background:rgba(40,28,26,.38);color:#fff;font-size:10px;font-weight:700;line-height:18px;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);max-width:82%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.end{padding:22px 0 calc(26px + var(--safe-b));text-align:center}
.end b{display:block;font-size:12px;font-weight:600;color:#C2ABA4;letter-spacing:2px}
.end small{display:block;margin-top:8px;font-size:10.5px;color:#CDBBB5;opacity:.85}
/* ---------- 封面(em 计:卡片 1em≈13px,详情放大) ---------- */
.cv{position:relative;width:100%;overflow:hidden;font-size:13px;color:var(--tk);isolation:isolate}
.cv .t1{position:relative;z-index:1;font-weight:900;line-height:1.25;letter-spacing:.04em;word-break:break-all}
.cv .t2{position:relative;z-index:1;font-weight:700;opacity:.82;line-height:1.4}
.cv i{font-style:normal}
/* s0 渐变 + 荧光笔 */
.cv.s0{background:linear-gradient(160deg,var(--c1),var(--c2));display:flex;flex-direction:column;justify-content:center;padding:0 1.2em}
.cv.s0 .t1{font-size:1.75em;color:#2A1E1E}
.cv.s0 .t1 span{background:linear-gradient(transparent 58%,rgba(255,255,255,.85) 58%,rgba(255,255,255,.85) 90%,transparent 90%)}
.cv.s0 .t2{margin-top:.7em;font-size:.95em}
.cv.s0 .dc{position:absolute;right:-1.6em;top:-1.6em;width:6em;height:6em;border-radius:50%;background:rgba(255,255,255,.35)}
.cv.s0 .dc2{position:absolute;left:1.2em;bottom:1.1em;width:2.4em;height:.32em;border-radius:1em;background:var(--tk);opacity:.5}
/* s1 便签纸 + 胶带 */
.cv.s1{background:radial-gradient(circle,rgba(255,255,255,.55) 1.2px,transparent 1.6px) 0 0/14px 14px,var(--c2);display:grid;place-items:center;padding:1.3em 1em}
.cv.s1 .pp{position:relative;width:100%;padding:1.3em .9em 1em;background:#FFFDF8;border-radius:.3em;box-shadow:0 .5em 1em -.4em rgba(80,40,30,.35);transform:rotate(-2.5deg)}
.cv.s1 .pp::before{content:"";position:absolute;left:50%;top:-.6em;width:4em;height:1.2em;margin-left:-2em;background:rgba(255,255,255,.65);border:1px solid rgba(0,0,0,.04);transform:rotate(3deg)}
.cv.s1 .t1{font-size:1.45em;color:#3A2C28}
.cv.s1 .t2{margin-top:.5em;font-size:.88em;color:var(--tk)}
/* s2 深色大引号 */
.cv.s2{background:radial-gradient(9em 7em at 100% 0,rgba(255,255,255,.18),transparent 70%),linear-gradient(160deg,var(--tk),#2E2230);color:#FFF4EA;display:flex;flex-direction:column;justify-content:flex-end;padding:1.1em 1.1em 1.2em}
.cv.s2 .q{position:absolute;right:.45em;top:-.15em;font-size:5.5em;font-weight:900;line-height:1;color:var(--c2);opacity:.75;font-family:Georgia,serif}
.cv.s2 .t1{font-size:1.6em}
.cv.s2 .t2{margin-top:.55em;font-size:.9em;color:var(--c2);opacity:1}
/* s3 横线本 */
.cv.s3{background:repeating-linear-gradient(180deg,transparent 0,transparent 1.75em,rgba(170,140,120,.22) 1.75em,rgba(170,140,120,.22) calc(1.75em + 1px)),#FFFCF6;padding:1.2em 1em 1em 1.9em;display:flex;flex-direction:column;justify-content:center}
.cv.s3::before{content:"";position:absolute;left:1.15em;top:0;bottom:0;width:1.5px;background:rgba(240,110,100,.45)}
.cv.s3 .t1{font-size:1.5em;color:#2F2424}
.cv.s3 .t1 span{background:linear-gradient(transparent 55%,var(--c2) 55%,var(--c2) 92%,transparent 92%)}
.cv.s3 .t2{margin-top:.7em;font-size:.9em;color:var(--tk)}
.cv.s3 .ck{display:flex;gap:.35em;margin-top:.8em}
.cv.s3 .ck i{width:.95em;height:.95em;border-radius:.25em;border:1.5px solid var(--tk);opacity:.6}
.cv.s3 .ck i:first-child{background:var(--tk)}
/* s4 大圆 + 水印字 */
.cv.s4{background:var(--c1);display:flex;flex-direction:column;justify-content:flex-end;padding:1em 1.1em 1.1em}
.cv.s4 .bc{position:absolute;right:-1.4em;top:-1.2em;width:8.4em;height:8.4em;border-radius:50%;background:linear-gradient(150deg,var(--c2),rgba(255,255,255,.4));display:grid;place-items:center}
.cv.s4 .bc b{font-size:4em;font-weight:900;color:#fff;opacity:.8;font-family:"STKaiti","KaiTi","Kaiti SC",serif;transform:translate(-.08em,.06em)}
.cv.s4 .t1{font-size:1.6em;color:#2A1E1E}
.cv.s4 .t2{margin-top:.45em;font-size:.9em}
/* s5 方格纸 + 印章 */
.cv.s5{background:linear-gradient(rgba(170,140,120,.13) 1px,transparent 1px) 0 0/1.2em 1.2em,linear-gradient(90deg,rgba(170,140,120,.13) 1px,transparent 1px) 0 0/1.2em 1.2em,#FFF9F3;display:flex;flex-direction:column;justify-content:center;padding:1em 1.1em}
.cv.s5 .st{align-self:flex-start;margin-bottom:.7em;padding:.2em .6em;border-radius:.4em;background:var(--tk);color:#fff;font-size:.8em;font-weight:800;letter-spacing:.08em;transform:rotate(-3deg)}
.cv.s5 .t1{font-size:1.55em;color:#2F2424}
.cv.s5 .t2{margin-top:.55em;font-size:.88em}
.cv.s5 .sm{position:absolute;right:.8em;bottom:.8em;width:3.2em;height:3.2em;border-radius:50%;border:2px solid var(--tk);color:var(--tk);display:grid;place-items:center;font-size:.8em;font-weight:900;opacity:.55;transform:rotate(-14deg)}
/* ---------- 详情层 ---------- */
#dt{position:fixed;inset:0;z-index:40;background:#fff;transform:translateX(100%);transition:transform .32s cubic-bezier(.2,.8,.2,1);display:flex;flex-direction:column}
#dt.show{transform:none}
#dt .dnav{flex:none;display:flex;align-items:center;gap:8px;height:calc(50px + env(safe-area-inset-top));padding:env(safe-area-inset-top) 12px 0 6px;background:#fff;border-bottom:1px solid transparent;transition:border-color .2s}
#dt .dnav.on{border-color:var(--line)}
#dt .dnav .bk{width:36px;height:36px;display:grid;place-items:center;border-radius:50%}
#dt .dnav .bk .ic{width:22px;height:22px}
#dt .dnav img{width:30px;height:30px;border-radius:50%;object-fit:cover;background:#FFE3D3}
#dt .dnav .who{flex:1;min-width:0;display:flex;flex-direction:column;line-height:1.25}
#dt .dnav .who b{font-size:14px;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
#dt .dnav .who small{font-size:11px;color:var(--muted)}
#dt .dnav .fo{flex:none;height:28px;padding:0 13px;border-radius:99px;border:1.5px solid #F4593C;color:#F4593C;font-size:12.5px;font-weight:800}
#dt .dnav .fo.on{border-color:#E5DAD5;color:var(--muted)}
#dsc{flex:1;min-height:0;overflow-y:auto;overflow-x:hidden;-webkit-overflow-scrolling:touch;overscroll-behavior:none}
#dsc::-webkit-scrollbar{display:none}
.dcv .cv{font-size:26px}
.dbody{padding:16px 18px 6px}
.dbody h2{margin:0;font-size:19px;font-weight:900;line-height:1.45;letter-spacing:.2px}
.dbody .tx{margin-top:10px;font-size:15px;line-height:1.85;color:#3A302C;letter-spacing:.2px}
.dbody .tx p{margin:0 0 10px}
.dbody .tgs{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px}
.dbody .tgs button{height:24px;padding:0 9px;border-radius:99px;background:var(--peach);color:#D9583C;font-size:12.5px;font-weight:700}
.dbody .tm{margin-top:12px;font-size:12px;color:#B8A49E}
.cm{margin-top:8px;padding:14px 18px 20px;border-top:8px solid #F8F3EF}
.cm-h{display:flex;align-items:baseline;gap:6px;font-size:15px;font-weight:900}
.cm-h small{font-size:12.5px;font-weight:500;color:var(--muted)}
.cm-in{display:flex;align-items:center;gap:8px;margin-top:12px}
.cm-in .me{width:30px;height:30px;border-radius:50%;flex:none;display:grid;place-items:center;background:linear-gradient(140deg,#FFC9B5,#F59A7F);color:#fff;font-size:12px;font-weight:800}
.cm-in button{flex:1;height:34px;padding:0 14px;border-radius:99px;background:#F5EEEA;text-align:left;font-size:13px;color:#B3A19B}
.ci{display:flex;gap:10px;padding:14px 0 12px;border-bottom:1px solid #F5EDE8}
.ci:last-child{border-bottom:0}
.ci>img,.ci>.me{width:32px;height:32px;border-radius:50%;object-fit:cover;flex:none;background:#FFE3D3}
.ci>.me{display:grid;place-items:center;background:linear-gradient(140deg,#FFC9B5,#F59A7F);color:#fff;font-size:12px;font-weight:800}
.ci.mine{animation:ncIn .35s ease both}
.ci-b{flex:1;min-width:0}
.ci-n{display:flex;align-items:center;gap:6px;font-size:12.5px;color:var(--muted)}
.ci-n b{color:#7A6A64;font-weight:700}
.ci-n .au{height:16px;padding:0 5px;border-radius:4px;background:#F4593C;color:#fff;font-size:10px;font-weight:800;line-height:16px}
.ci-n .mt{height:16px;padding:0 5px;border-radius:4px;background:#FFE7E0;color:#EC4D33;font-size:10px;font-weight:800;line-height:16px}
.ci-n .lk{margin-left:auto}
.ci-t{margin:3px 0 0;font-size:14.5px;line-height:1.65;color:#2F2624;word-break:break-word}
.ci-s{margin-top:3px;font-size:11.5px;color:#B8A49E}
.rp{margin-top:8px;padding:8px 10px;border-radius:10px;background:#F8F3EF;font-size:13px;line-height:1.65;color:#3A302C}
.rp p{margin:0 0 4px}
.rp p:last-child{margin-bottom:0}
.rp b{font-weight:700;color:#7A6A64}
.rp .au{display:inline-block;height:15px;margin:0 3px 0 -1px;padding:0 4px;border-radius:4px;background:#F4593C;color:#fff;font-size:9.5px;font-weight:800;line-height:15px;vertical-align:1px}
.cm-end{padding:18px 0 6px;text-align:center;font-size:12px;color:#C9B7B1}
.bar{flex:none;display:flex;align-items:center;gap:6px;padding:8px 10px calc(8px + var(--safe-b)) 14px;background:#fff;border-top:1px solid var(--line)}
.bar .ip{flex:1;min-width:0;display:flex;align-items:center;gap:6px;height:38px;padding:0 6px 0 14px;border-radius:99px;background:#F5EEEA}
.bar .ip .ic{width:15px;height:15px;color:#B3A19B}
.bar input{flex:1;min-width:0;height:100%;border:0;background:none;outline:none;font:inherit;font-size:14px;color:var(--ink)}
.bar input::placeholder{color:#B3A19B}
.bar .send{flex:none;height:28px;padding:0 12px;border-radius:99px;background:var(--btn);color:#fff;font-size:13px;font-weight:800}
.bar .send:disabled{opacity:.4}
.bar .ib{flex:none;display:flex;align-items:center;gap:3px;height:38px;padding:0 6px;font-size:13px;color:#5E504C;font-variant-numeric:tabular-nums}
.bar .ib .ic{width:23px;height:23px}
.bar .ib.on.lv{color:#F0453A}
.bar .ib.on.fv{color:#F2A516}
.bar .ib.pop .ic{animation:pop .35s ease}
.bar.typing .ib{display:none}
.bar:not(.typing) .send{display:none}
.toast{position:fixed;left:50%;top:44%;z-index:80;max-width:280px;padding:10px 16px;border-radius:12px;background:rgba(40,30,28,.86);color:#fff;font-size:13.5px;line-height:1.6;text-align:center;transform:translate(-50%,8px);opacity:0;pointer-events:none;transition:opacity .2s,transform .2s}
.toast.show{opacity:1;transform:translate(-50%,0)}
</style>
</head>
<body>
<svg width="0" height="0" style="position:absolute" aria-hidden="true">
<symbol id="i-back" viewBox="0 0 24 24"><path d="M15 5l-7 7 7 7" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/></symbol>
<symbol id="i-heart" viewBox="0 0 24 24"><path d="M12 20.3s-7.6-4.6-9.2-9.4C1.7 7.6 3.9 4.5 7.2 4.5c2 0 3.6 1.1 4.8 2.8 1.2-1.7 2.8-2.8 4.8-2.8 3.3 0 5.5 3.1 4.4 6.4-1.6 4.8-9.2 9.4-9.2 9.4z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></symbol>
<symbol id="i-heart-f" viewBox="0 0 24 24"><path d="M12 20.3s-7.6-4.6-9.2-9.4C1.7 7.6 3.9 4.5 7.2 4.5c2 0 3.6 1.1 4.8 2.8 1.2-1.7 2.8-2.8 4.8-2.8 3.3 0 5.5 3.1 4.4 6.4-1.6 4.8-9.2 9.4-9.2 9.4z" fill="currentColor"/></symbol>
<symbol id="i-star" viewBox="0 0 24 24"><path d="M12 3.6l2.6 5.3 5.8.8-4.2 4.1 1 5.8L12 16.9l-5.2 2.7 1-5.8-4.2-4.1 5.8-.8z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></symbol>
<symbol id="i-star-f" viewBox="0 0 24 24"><path d="M12 3.6l2.6 5.3 5.8.8-4.2 4.1 1 5.8L12 16.9l-5.2 2.7 1-5.8-4.2-4.1 5.8-.8z" fill="currentColor"/></symbol>
<symbol id="i-chat" viewBox="0 0 24 24"><path d="M4.5 18.8V7.6A3 3 0 017.5 4.6h9a3 3 0 013 3v6a3 3 0 01-3 3H8.8z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></symbol>
<symbol id="i-pen" viewBox="0 0 24 24"><path d="M4 20h4L19 9l-4-4L4 16z" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/></symbol>
<symbol id="i-plus" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></symbol>
</svg>
<div id="sc">
<div class="top" id="top">
<div class="nav">
<button class="bk" id="back" type="button" aria-label="返回"><svg class="ic"><use href="#i-back"/></svg></button>
<h1><span>孕妈广场</span></h1>
<span class="sp"></span>
</div>
<div class="tabs" id="tabs" role="tablist"></div>
</div>
<div id="head"></div>
<div class="wf" id="wf"><div class="col"></div><div class="col"></div></div>
<div class="end"><b>— 到底啦 —</b><small>内容为示例占位,正式版将替换为妈妈们的真实分享</small></div>
</div>
<div id="dt" aria-hidden="true">
<div class="dnav" id="dnav">
<button class="bk" id="dback" type="button" aria-label="返回"><svg class="ic"><use href="#i-back"/></svg></button>
<img id="dav" alt="">
<span class="who"><b id="dnm"></b><small id="dst"></small></span>
<button class="fo" id="dfo" type="button">关注</button>
</div>
<div id="dsc"></div>
<div class="bar" id="bar">
<label class="ip"><svg class="ic"><use href="#i-pen"/></svg><input id="cin" type="text" maxlength="200" placeholder="说点什么…" enterkeyhint="send"><button class="send" id="csend" type="button" disabled>发送</button></label>
<button class="ib lv" id="blk" type="button" aria-label="点赞"><svg class="ic"><use href="#i-heart"/></svg><span></span></button>
<button class="ib fv" id="bfv" type="button" aria-label="收藏"><svg class="ic"><use href="#i-star"/></svg><span></span></button>
<button class="ib" id="bcm" type="button" aria-label="评论"><svg class="ic"><use href="#i-chat"/></svg><span></span></button>
</div>
</div>
<div class="toast" id="toast"></div>
<script src="topics.js?v=26110109"></script>
<script src="data.js?v=26110109"></script>
<script>
(function () {
'use strict';
var $ = function (s, c) { return (c || document).querySelector(s); };
var T = window.GC_TOPICS || [], D = window.GC_DATA || {};
var TBY = {}; T.forEach(function (t) { TBY[t.id] = t; });
var esc = function (s) { return String(s == null ? '' : s).replace(/[&<>"]/g, function (c) { return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[c]; }); };
var seed = function (t) { var x = 7; t = String(t); for (var i = 0; i < t.length; i++) x = (x * 31 + t.charCodeAt(i)) | 0; return Math.abs(x); };
var kfmt = function (n) { return n >= 10000 ? (n / 10000).toFixed(1).replace(/\.0$/, '') + '万' : n >= 1000 ? (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k' : String(n); };
/* 点赞 / 收藏 / 评论数:一万以内写实数,点一下能看见 +1 */
var cfmt = function (n) { return n >= 10000 ? (n / 10000).toFixed(1).replace(/\.0$/, '') + '万' : String(n); };
function rng(s) { var x = seed(s) || 1; return function () { x = (x * 1103515245 + 12345) & 0x7fffffff; return x / 0x7fffffff; }; }
/* ---------- 身份:同首页 hashParam,hash 优先、query 兜底 ---------- */
function param(name) {
try {
var h = (location.hash.slice(1) || '') + '&' + (location.search.slice(1) || '');
var m = h.match(new RegExp('(?:^|[&#])' + name + '=([^&#]*)'));
return m ? decodeURIComponent(m[1]).split('?')[0] : '';
} catch (e) { return ''; }
}
var OPENID = param('openid');
var MYNAME = param('nickname') || '孕妈'; /* 授权回填的昵称在 hash 里;没有就叫「孕妈」,后面另挂「我」标 */
/* ---------- 头像:沿用首页 assets/img/mom/ 写实头像 ---------- */
var AV = ['m01','m02','m03','m04','m05','m06','m07','m08','m09','m10','m11','m12','m13','m14','m15','m16','m17','m18','m19','m20','m21','m22',
'p01','p02','p03','p04','p05','p06','p07','p08','p09','p10','p11','p12','p13','p14','s01','s02','s03','s04','s05','s06','s07','s08','s09','s10',
'k01','k02','k03','k04','k05','k06','k07','k08','k09','k10','k11','k12','k13'];
var avSrc = function (key) { return '../assets/img/mom/' + AV[seed(key) % AV.length] + '.jpg'; };
/* ---------- 笔记:data.js 的原始内容 + 确定性补字段(id / 封面样式 / 比例 / 评论) ---------- */
var NOTES = [], NBY = {};
var RATIO = [1.25, 0.96, 1.16, 0.84, 1.32, 1.06, 0.92];
T.forEach(function (t, ti) {
var d = D[t.id]; if (!d) return;
(d.notes || []).forEach(function (n, i) {
var o = {
id: t.id + '-' + (i + 1), t: t.id, i: i, title: n.title, cover: n.cover || n.title, coverSub: n.coverSub || '',
author: n.author, stage: n.stage, likes: +n.likes || 0, body: n.body || '',
style: (ti * 2 + i * 5) % 6, ratio: RATIO[(ti + i * 3) % RATIO.length],
/* 封面配色:三条里一条用本话题色,另两条借别的话题色,同一话题页里不至于一片同色 */
pal: i % 3 === 0 ? t : T[(ti + 3 + i * 4) % T.length]
};
NOTES.push(o); NBY[o.id] = o;
});
});
/* 「全部」的混排:各话题轮流取一条(同一话题不扎堆),每轮按 id 哈希打散 */
var MIXED = (function () {
var out = [], byT = T.map(function (t) { return NOTES.filter(function (n) { return n.t === t.id; }); });
for (var r = 0; r < 10; r++) {
var round = [];
byT.forEach(function (l) { if (l[r]) round.push(l[r]); });
round.sort(function (a, b) { return seed(a.id + 'm') % 97 - seed(b.id + 'm') % 97; });
out = out.concat(round);
}
return out;
})();
var AGO = ['3 分钟前', '18 分钟前', '1 小时前', '2 小时前', '5 小时前', '昨天 21:40', '昨天 08:12', '10-29', '10-27', '10-25'];
function commentsOf(n) {
var pool = (D[n.t] && D[n.t].comments) || [], r = rng(n.id + 'c');
var idx = pool.map(function (_, k) { return k; });
for (var k = idx.length - 1; k > 0; k--) { var j = Math.floor(r() * (k + 1)); var x = idx[k]; idx[k] = idx[j]; idx[j] = x; }
var cnt = Math.min(pool.length, 3 + (seed(n.id) % 6));
return idx.slice(0, cnt).map(function (k, m) {
var c = pool[k];
return { key: n.id + '#' + k, n: c.n, s: c.s, t: c.t, likes: Math.floor(r() * 180) + 2, ago: AGO[Math.min(AGO.length - 1, m * 2 + Math.floor(r() * 2))],
r: (c.r || []).map(function (x, q) { return { n: x.n, t: x.t, au: q === 0 && r() < .35 }; }) };
});
}
/* ---------- 本机存储 mmbox.gc.v1:{ <openid|_guest>: { like:{}, fav:{}, cl:{}, cm:{id:[{t,ts}]} } } ---------- */
var SK = 'mmbox.gc.v1', UK = OPENID || '_guest';
function loadAll() { try { return JSON.parse(localStorage.getItem(SK) || '{}') || {}; } catch (e) { return {}; } }
var ALL = loadAll();
var ME = ALL[UK] || (ALL[UK] = {});
['like', 'fav', 'cl', 'cm', 'fo'].forEach(function (k) { if (!ME[k] || typeof ME[k] !== 'object') ME[k] = {}; });
function save() { try { ALL[UK] = ME; localStorage.setItem(SK, JSON.stringify(ALL)); } catch (e) {} }
var liked = function (id) { return !!ME.like[id]; };
var likeN = function (n) { return n.likes + (liked(n.id) ? 1 : 0); };
var favN = function (n) { return Math.round(n.likes * .38) + 3 + (ME.fav[n.id] ? 1 : 0); };
var myCm = function (id) { return ME.cm[id] || []; };
var cmN = function (n) { return commentsOf(n).length + myCm(n.id).length; };
function toast(msg) {
var t = $('#toast'); t.textContent = msg; t.classList.add('show');
clearTimeout(toast._t); toast._t = setTimeout(function () { t.classList.remove('show'); }, 1800);
}
function haptic() { if (navigator.vibrate) navigator.vibrate(8); }
/* ---------- 封面 ---------- */
function coverHTML(n, h) {
var t = TBY[n.t], pl = n.pal || t, st = 'style="--c1:' + pl.c[0] + ';--c2:' + pl.c[1] + ';--tk:' + pl.ink + ';' + (h ? 'height:' + h : '') + '"';
var t1 = esc(n.cover), t2 = esc(n.coverSub), inner;
switch (n.style) {
case 0: inner = '<i class="dc"></i><i class="dc2"></i><div class="t1"><span>' + t1 + '</span></div><div class="t2">' + t2 + '</div>'; break;
case 1: inner = '<div class="pp"><div class="t1">' + t1 + '</div><div class="t2">' + t2 + '</div></div>'; break;
case 2: inner = '<i class="q">“</i><div class="t1">' + t1 + '</div><div class="t2">' + t2 + '</div>'; break;
case 3: inner = '<div class="t1"><span>' + t1 + '</span></div><div class="t2">' + t2 + '</div><div class="ck"><i></i><i></i><i></i></div>'; break;
case 4: inner = '<i class="bc"><b>' + esc(t.g) + '</b></i><div class="t1">' + t1 + '</div><div class="t2">' + t2 + '</div>'; break;
default: inner = '<span class="st">' + (t.cat === 'family' ? '家有暖事' : '过来人经验') + '</span><div class="t1">' + t1 + '</div><div class="t2">' + t2 + '</div><i class="sm">' + esc(t.g) + '</i>';
}
return '<div class="cv s' + n.style + '" ' + st + '>' + inner + '</div>';
}
function likeHTML(n) {
var on = liked(n.id);
return '<span class="lk' + (on ? ' on' : '') + '" data-like="' + n.id + '" role="button" aria-label="点赞"><svg class="ic"><use href="#i-heart' + (on ? '-f' : '') + '"/></svg>' + cfmt(likeN(n)) + '</span>';
}
function cardHTML(n, showTopic) {
return '<button type="button" class="nc" data-id="' + n.id + '"><div class="cvw">' +
(showTopic ? '<span class="ttag"># ' + esc(TBY[n.t].name) + '</span>' : '') +
coverHTML(n, 'calc(var(--cw) * ' + n.ratio + ')') + '</div>' +
'<div class="nb"><h3>' + esc(n.title) + '</h3><div class="nf"><img src="' + avSrc(n.author) + '" alt="" loading="lazy"><span class="nm">' + esc(n.author) + '</span>' + likeHTML(n) + '</div></div></button>';
}
/* ---------- 列表 ---------- */
var cur = '', catF = '';
function renderTabs() {
var h = '<button type="button" data-t="" class="' + (cur ? '' : 'on') + '">全部</button>' + T.map(function (t) {
return '<button type="button" data-t="' + t.id + '" class="' + (cur === t.id ? 'on' : '') + '">' + esc(t.name) + (t.hot ? '<i class="hb">热</i>' : '') + '</button>';
}).join('');
$('#tabs').innerHTML = h;
}
function renderHead() {
var t = TBY[cur], el = $('#head');
if (!t) {
el.innerHTML = '<div class="intro"><span><b>' + T.length + '</b> 个话题 · 都是孕妈们的真心话</span><span class="seg" id="seg">' +
[['', '推荐'], ['family', '家庭'], ['exp', '经验']].map(function (x) { return '<button type="button" data-c="' + x[0] + '" class="' + (catF === x[0] ? 'on' : '') + '">' + x[1] + '</button>'; }).join('') + '</span></div>';
return;
}
var d = D[t.id] || {};
var avs = [0, 1, 2, 3].map(function (k) { return '<img src="' + avSrc(t.id + k) + '" alt="">'; }).join('');
el.innerHTML = '<div class="th" style="--c1:' + t.c[0] + ';--c2:' + t.c[1] + ';--tk:' + t.ink + '"><span class="tg" aria-hidden="true">' + esc(t.g) + '</span>' +
'<span class="tc">' + (t.cat === 'family' ? '家庭关系' : '宝妈经验') + (t.hot ? ' · 热门' : '') + '</span>' +
'<h2><em>#</em>' + esc(t.name) + '</h2><p>' + esc(d.desc || '') + '</p>' +
'<div class="tf"><span class="ts"><span class="avs">' + avs + '</span><span><b>' + kfmt(t.n) + '</b> 篇笔记 · <b>' + kfmt(t.talk) + '</b> 人在聊</span></span>' +
'<button type="button" class="join" id="join"><svg class="ic"><use href="#i-plus"/></svg>参与话题</button></div></div>';
}
function renderFeed() {
var list = cur ? NOTES.filter(function (n) { return n.t === cur; }) : MIXED.filter(function (n) { return !catF || TBY[n.t].cat === catF; });
var wf = $('#wf'), cols = wf.children;
cols[0].innerHTML = ''; cols[1].innerHTML = '';
var cw = cols[0].clientWidth || 175;
wf.style.setProperty('--cw', cw + 'px');
/* 真瀑布流:每张卡放进当前更矮的那一列(逐张量高,标题一行/两行也算进去) */
var hs = [0, 0], tmp = document.createElement('div');
list.forEach(function (n, k) {
tmp.innerHTML = cardHTML(n, !cur);
var el = tmp.firstChild; el.style.animationDelay = Math.min(k, 10) * 30 + 'ms';
var c = hs[0] <= hs[1] ? 0 : 1;
cols[c].appendChild(el);
hs[c] += el.offsetHeight + 8;
});
}
function setQuery(t, n) {
var q = new URLSearchParams(location.search);
if (t) q.set('t', t); else q.delete('t');
if (n) q.set('n', n); else q.delete('n');
var s = q.toString();
return location.pathname + (s ? '?' + s : '') + location.hash; /* hash(身份)原样保留 */
}
function selectTopic(t, smooth) {
if (!TBY[t]) t = '';
cur = t;
renderTabs(); renderHead(); renderFeed();
history.replaceState(history.state, '', setQuery(cur, ''));
var on = $('#tabs .on');
if (on) { var tb = $('#tabs'); tb.scrollLeft = Math.max(0, on.offsetLeft - tb.clientWidth / 2 + on.offsetWidth / 2); }
var sc = $('#sc'); if (smooth !== false) sc.scrollTop = 0;
}
$('#tabs').addEventListener('click', function (e) {
var b = e.target.closest('button[data-t]');
if (b && b.dataset.t !== cur) { haptic(); selectTopic(b.dataset.t); }
});
$('#head').addEventListener('click', function (e) {
var s = e.target.closest('#seg button');
if (s && s.dataset.c !== catF) { catF = s.dataset.c; haptic(); renderHead(); renderFeed(); return; }
if (e.target.closest('#join')) toast('发布笔记马上开放,先去看看大家怎么说吧');
});
/* 卡片:点心形就地点赞,点别处进详情 */
function toggleLike(id) {
if (ME.like[id]) delete ME.like[id]; else ME.like[id] = 1;
save(); haptic();
}
function syncCardLike(id) {
var n = NBY[id];
document.querySelectorAll('.lk[data-like="' + id + '"]').forEach(function (el) {
var on = liked(id);
el.classList.toggle('on', on);
el.innerHTML = '<svg class="ic"><use href="#i-heart' + (on ? '-f' : '') + '"/></svg>' + cfmt(likeN(n));
if (on) { el.classList.remove('pop'); void el.offsetWidth; el.classList.add('pop'); }
});
}
$('#wf').addEventListener('click', function (e) {
var lk = e.target.closest('[data-like]');
if (lk) { e.stopPropagation(); toggleLike(lk.dataset.like); syncCardLike(lk.dataset.like); return; }
var c = e.target.closest('.nc');
if (c) openNote(c.dataset.id, true);
});
/* ---------- 详情 ---------- */
var openId = '', pushed = false;
function bodyHTML(s) { return String(s).split(/\n+/).filter(Boolean).map(function (p) { return '<p>' + esc(p) + '</p>'; }).join(''); }
function commentHTML(c, mine) {
if (mine) {
return '<div class="ci mine"><span class="me">我</span><div class="ci-b"><div class="ci-n"><b>' + esc(MYNAME) + '</b><span class="mt">我</span></div>' +
'<p class="ci-t">' + esc(c.t) + '</p><div class="ci-s">' + agoOf(c.ts) + '</div></div></div>';
}
var on = !!ME.cl[c.key], n = c.likes + (on ? 1 : 0);
var rp = c.r.length ? '<div class="rp">' + c.r.map(function (x) { return '<p><b>' + esc(x.n) + '</b>' + (x.au ? '<span class="au">作者</span>' : '') + ':' + esc(x.t) + '</p>'; }).join('') + '</div>' : '';
return '<div class="ci"><img src="' + avSrc(c.n) + '" alt="" loading="lazy"><div class="ci-b"><div class="ci-n"><b>' + esc(c.n) + '</b>' +
'<span class="lk' + (on ? ' on' : '') + '" data-cl="' + esc(c.key) + '" data-base="' + c.likes + '" role="button"><svg class="ic"><use href="#i-heart' + (on ? '-f' : '') + '"/></svg>' + n + '</span></div>' +
'<p class="ci-t">' + esc(c.t) + '</p><div class="ci-s">' + esc(c.s || '') + ' · ' + c.ago + '</div>' + rp + '</div></div>';
}
function agoOf(ts) {
var s = Math.max(0, (Date.now() - ts) / 1000);
if (s < 60) return '刚刚';
if (s < 3600) return Math.floor(s / 60) + ' 分钟前';
if (s < 86400) return Math.floor(s / 3600) + ' 小时前';
var d = new Date(ts); return (d.getMonth() + 1) + '-' + d.getDate();
}
function renderComments(n) {
var mine = myCm(n.id).slice().reverse(), cs = commentsOf(n);
$('#cmh').innerHTML = '评论 <small>共 ' + cmN(n) + ' 条</small>';
$('#cml').innerHTML = mine.map(function (c) { return commentHTML(c, true); }).join('') + cs.map(function (c) { return commentHTML(c); }).join('');
}
function syncBar(n, popK) {
var lv = $('#blk'), fv = $('#bfv'), on = liked(n.id), fo = !!ME.fav[n.id];
lv.classList.toggle('on', on); lv.querySelector('use').setAttribute('href', on ? '#i-heart-f' : '#i-heart'); lv.querySelector('span').textContent = cfmt(likeN(n));
fv.classList.toggle('on', fo); fv.querySelector('use').setAttribute('href', fo ? '#i-star-f' : '#i-star'); fv.querySelector('span').textContent = cfmt(favN(n));
$('#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'); }
}
function openNote(id, push) {
var n = NBY[id]; if (!n) return;
var t = TBY[n.t];
openId = id;
$('#dav').src = avSrc(n.author);
$('#dnm').textContent = n.author;
$('#dst').textContent = n.stage || '';
var fo = $('#dfo'); fo.classList.toggle('on', !!ME.fo[n.author]); fo.textContent = ME.fo[n.author] ? '已关注' : '关注';
var h = Math.round(window.innerWidth * Math.min(n.ratio, 1.05));
var day = 26 + (seed(n.id) % 6), mon = day > 31 ? 11 : 10;
$('#dsc').innerHTML = '<div class="dcv">' + coverHTML(n, h + 'px') + '</div>' +
'<div class="dbody"><h2>' + esc(n.title) + '</h2><div class="tx">' + bodyHTML(n.body) + '</div>' +
'<div class="tgs"><button type="button" data-go="' + t.id + '"># ' + esc(t.name) + '</button><button type="button" data-go="">#孕妈广场</button></div>' +
'<div class="tm">' + mon + '-' + (day > 31 ? day - 31 : day) + ' · ' + esc(n.stage || '') + '</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">留下你的温柔一句…</button></div>' +
'<div id="cml"></div><div class="cm-end">— 没有更多评论了 —</div></div>';
renderComments(n); syncBar(n);
$('#cin').value = ''; $('#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: id }, '', setQuery(cur, id)); pushed = true; }
else history.replaceState(history.state, '', setQuery(cur, id));
}
function hideNote() {
var dt = $('#dt'); dt.classList.remove('show'); dt.setAttribute('aria-hidden', 'true');
$('#cin').blur();
var id = openId; openId = '';
if (id) syncCardLike(id);
}
function closeNote() {
if (!openId) return;
if (pushed) { history.back(); return; } /* popstate 里 hideNote */
hideNote(); history.replaceState(history.state, '', setQuery(cur, ''));
}
window.addEventListener('popstate', function () {
pushed = false;
if (openId) hideNote();
});
$('#dback').addEventListener('click', closeNote);
$('#dfo').addEventListener('click', function () {
var n = NBY[openId]; if (!n) return;
if (ME.fo[n.author]) delete ME.fo[n.author]; else ME.fo[n.author] = 1;
save(); haptic();
this.classList.toggle('on', !!ME.fo[n.author]); this.textContent = ME.fo[n.author] ? '已关注' : '关注';
});
$('#dsc').addEventListener('scroll', function () { $('#dnav').classList.toggle('on', this.scrollTop > 8); }, { passive: true });
$('#dsc').addEventListener('click', function (e) {
var g = e.target.closest('[data-go]');
if (g) { var to = g.dataset.go; closeNote(); setTimeout(function () { selectTopic(to); }, 60); return; }
var cl = e.target.closest('[data-cl]');
if (cl) {
var k = cl.dataset.cl, on;
if (ME.cl[k]) delete ME.cl[k]; else ME.cl[k] = 1;
save(); haptic(); on = !!ME.cl[k];
cl.classList.toggle('on', on);
cl.innerHTML = '<svg class="ic"><use href="#i-heart' + (on ? '-f' : '') + '"/></svg>' + (+cl.dataset.base + (on ? 1 : 0));
if (on) { cl.classList.remove('pop'); void cl.offsetWidth; cl.classList.add('pop'); }
return;
}
if (e.target.closest('#cmgo')) $('#cin').focus();
});
$('#blk').addEventListener('click', function () {
var n = NBY[openId]; if (!n) return;
toggleLike(n.id); syncBar(n, liked(n.id) ? 'lv' : '');
});
$('#bfv').addEventListener('click', function () {
var n = NBY[openId]; if (!n) return;
if (ME.fav[n.id]) delete ME.fav[n.id]; else ME.fav[n.id] = 1;
save(); haptic(); syncBar(n, ME.fav[n.id] ? 'fv' : '');
toast(ME.fav[n.id] ? '已收藏,在这台手机上随时能找到' : '已取消收藏');
});
$('#bcm').addEventListener('click', function () {
var w = $('#cmw'); if (w) $('#dsc').scrollTo({ top: w.offsetTop - 50, behavior: 'smooth' });
});
var cin = $('#cin');
cin.addEventListener('focus', function () { $('#bar').classList.add('typing'); });
cin.addEventListener('blur', function () { setTimeout(function () { if (!cin.value.trim()) $('#bar').classList.remove('typing'); }, 120); });
cin.addEventListener('input', function () { $('#csend').disabled = !cin.value.trim(); });
cin.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); send(); } });
$('#csend').addEventListener('click', send);
function send() {
var n = NBY[openId], v = cin.value.trim();
if (!n || !v) return;
(ME.cm[n.id] = myCm(n.id)).push({ t: v.slice(0, 200), ts: Date.now() });
save(); haptic();
cin.value = ''; $('#csend').disabled = true; cin.blur(); $('#bar').classList.remove('typing');
renderComments(n); syncBar(n);
var w = $('#cmw'); if (w) $('#dsc').scrollTo({ top: w.offsetTop - 50, behavior: 'smooth' });
toast('评论已发出');
}
/* ---------- 返回 ---------- */
function homeURL() {
var q = new URLSearchParams(location.search); q.delete('t'); q.delete('n');
var s = q.toString();
return '../' + (s ? '?' + s : '') + location.hash;
}
$('#back').addEventListener('click', function () {
var fromHome = false;
try { fromHome = !!document.referrer && new URL(document.referrer).origin === location.origin; } catch (e) {}
if (fromHome && history.length > 1) {
history.back();
/* 后退没生效(宿主新开的 web-view 没有站内历史)→ 直接回首页 */
setTimeout(function () { location.href = homeURL(); }, 500);
} else location.href = homeURL();
});
/* ---------- 吸顶阴影 ---------- */
var sc = $('#sc');
sc.addEventListener('scroll', function () { $('#top').classList.toggle('on', sc.scrollTop > 4); }, { passive: true });
/* ---------- 禁止下拉回弹(同首页那段):竖向拖动时从手指下往上找还能朝该方向滚的容器,找不到才拦 ---------- */
var sx = 0, sy = 0;
document.addEventListener('touchstart', function (e) {
if (e.touches.length !== 1) return;
sx = e.touches[0].clientX; sy = e.touches[0].clientY;
}, { passive: true });
document.addEventListener('touchmove', function (e) {
if (e.touches.length !== 1) { if (e.cancelable) e.preventDefault(); return; }
var dx = e.touches[0].clientX - sx, dy = e.touches[0].clientY - sy;
if (Math.abs(dy) <= Math.abs(dx)) return;
var el = e.target;
while (el && el !== document.body && el !== document.documentElement) {
if (el.nodeType === 1) {
var oy = getComputedStyle(el).overflowY;
if ((oy === 'auto' || oy === 'scroll') && el.scrollHeight > el.clientHeight + 1) {
if (dy > 0 && el.scrollTop > 0) return;
if (dy < 0 && el.scrollTop + el.clientHeight < el.scrollHeight - 1) return;
}
}
el = el.parentNode;
}
if (e.cancelable) e.preventDefault();
}, { passive: false });
/* ---------- 启动 ---------- */
var deep = new URLSearchParams(location.search).get('n'); /* 先读:selectTopic 会 replaceState 把 n 去掉 */
selectTopic(param('t') && TBY[param('t')] ? param('t') : '', false);
if (deep && NBY[deep]) openNote(deep, false);
var rt; window.addEventListener('resize', function () { clearTimeout(rt); rt = setTimeout(renderFeed, 150); });
})();
</script>
</body>
</html>

22
guangchang/topics.js Normal file
View File

@@ -0,0 +1,22 @@
/* 孕妈广场 · 话题表(2026-11-01 第一版,占位内容,纯前端)
首页「孕妈广场」横滑卡(js/app.js initPlaza)和广场页(guangchang/index.html)共用这一份。
⚠️ 篇数 / 在聊人数是占位数字,正式版接后端后替换;广场页底部有一行「示例占位」说明。
字段:id 话题 id(广场页 ?t=)· name 名称 · cat family 家庭关系 / exp 宝妈经验 · g 封面水印字
c 渐变两色 · ink 主题深色(标题、角标)· n 篇数 · talk 在聊人数 · hot 热门角标 */
window.GC_TOPICS = [
{ id: 'laogong-nuan', name: '老公做过最暖的事', cat: 'family', g: '暖', c: ['#FFD9C7', '#FFB59A'], ink: '#B5462B', n: 1286, talk: 5320, hot: 1 },
{ id: 'yuntu', name: '孕吐怎么熬过去的', cat: 'exp', g: '吐', c: ['#DDF3E4', '#A9DDBE'], ink: '#2F7A55', n: 932, talk: 3870, hot: 1 },
{ id: 'popo-mama', name: '婆婆和妈妈的爱', cat: 'family', g: '爱', c: ['#FFE0E8', '#FFB8CB'], ink: '#B23E62', n: 864, talk: 3105 },
{ id: 'daichanbao', name: '待产包到底带什么', cat: 'exp', g: '包', c: ['#FFF0C9', '#FFD98A'], ink: '#9A6A0E', n: 1045, talk: 4260, hot: 1 },
{ id: 'baba-taidong', name: '爸爸的第一次胎动', cat: 'family', g: '动', c: ['#DCEBFF', '#AFCBF5'], ink: '#3A5F9E', n: 518, talk: 1984 },
{ id: 'fenmian', name: '顺产/剖宫产真实经历', cat: 'exp', g: '生', c: ['#EDE4FF', '#C9B6F5'], ink: '#6447B5', n: 1172, talk: 4980, hot: 1 },
{ id: 'popo-xiangchu', name: '和婆婆相处那些事', cat: 'family', g: '婆', c: ['#FFE6D2', '#F8C39B'], ink: '#A45524', n: 740, talk: 2890 },
{ id: 'chanjian', name: '产检省时攻略', cat: 'exp', g: '检', c: ['#D7F2F2', '#9FDADA'], ink: '#24777A', n: 456, talk: 1720 },
{ id: 'chaojia-hehao', name: '孕期吵架和好记', cat: 'family', g: '和', c: ['#FFE3DC', '#F7B2A6'], ink: '#B04434', n: 392, talk: 1655 },
{ id: 'haowu-cailei', name: '孕期好物/踩雷清单', cat: 'exp', g: '物', c: ['#FFEBD6', '#FFC995'], ink: '#A0561A', n: 1310, talk: 5012 },
{ id: 'jiaren-mihuo', name: '家里人的迷惑行为', cat: 'family', g: '囧', c: ['#F3EFD6', '#E2D79A'], ink: '#7D6E1C', n: 603, talk: 2410 },
{ id: 'shuimian', name: '孕期睡眠姿势', cat: 'exp', g: '眠', c: ['#E1E6FF', '#B4BEF2'], ink: '#4A55A8', n: 488, talk: 1890 },
{ id: 'qiming-dahui', name: '给宝宝起名大会', cat: 'family', g: '名', c: ['#FCE4F6', '#EDB5DF'], ink: '#9A3C82', n: 822, talk: 3366 },
{ id: 'zhichang', name: '职场孕妈生存指南', cat: 'exp', g: '职', c: ['#E2EEF6', '#AFCBDF'], ink: '#3D6680', n: 367, talk: 1402 },
{ id: 'chuanda', name: '孕期穿搭与身材', cat: 'exp', g: '衣', c: ['#FFE4EC', '#F9B9C9'], ink: '#A9405E', n: 579, talk: 2233 }
];

View File

@@ -7,7 +7,7 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>兜爸心选 · 孕育陪伴</title>
<!-- V:26110108 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<!-- V:26110109 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<link rel="stylesheet" href="mama-box.css">
<link rel="stylesheet" href="night.css">
<!-- 夜晚模式:首屏绘制前就按上次的选择加 class,避免先白后黑闪一下(开关在首页右上角 #nightBtn,逻辑见 app.js) -->
@@ -447,6 +447,10 @@
html.night .hts-row,html.night .hts-add{background:rgba(255,255,255,.07);box-shadow:none}
html.night .hts-mv,html.night .hts-reset{background:rgba(255,255,255,.1);color:#E2D2DC}
html.night .hts-foot{border-color:rgba(255,255,255,.08)}
/* 2026-11-01 用户:「把此刻下面三行轮播改成两行轮播」→ 可见 2 行(app.js FEED_N=2),高度 30×2;
mama-box.css 里「新进一行淡入」按第 4 个子元素写死,两行时新进的是第 3 个,这里补上 */
.pl-feed.pl-feed3{height:60px}
.pl-feed3 .pl-track>.pl-row:nth-child(3):last-child{animation:plIn .55s var(--ease) both}
</style>
<!-- 此刻(2026-10-01 需求文档 v1.0 第二节;同日两次改版)。数据 GET /api/mp/public/mama-box/pulse,渲染在 app.js pulseCard()。
⚠️ 模拟数据是产品负责人 2026-10-01 拍板的例外(AGENTS.md §4.12),只限这一条。
@@ -491,6 +495,57 @@
</div>
</section>
<!-- 孕妈广场(2026-11-01 第一版,占位内容)。用户原话:「在这个模块下边加一个广场,放一些话题,有跟家庭里的,也有宝妈经验的……
展示的时候不要像下面『其他妈妈也在搜』那种列表形式,两个列表就乱了。点进去之后变成小红书那种瀑布流」。
→ 首页只放一排横滑的话题封面卡(不是列表):渐变色块 + 大水印字 + 话题名 + 篇数/在聊,热门带角标;
点卡进 guangchang/?t=话题id,点「去逛逛」进广场全部。话题表在 guangchang/topics.js(与广场页共用),渲染在 app.js initPlaza()。
⚠️ 篇数 / 在聊人数是占位数字(topics.js 注释),广场页底部写着「示例占位」。topics.js 没加载到 → 整块保持 .hide。 -->
<section class="gc-home hide" id="gcHome">
<div class="gch-head">
<h2 class="gch-title"><span>孕妈广场</span></h2>
<span class="gch-cat" id="gchCat" role="tablist" aria-label="话题分类"><button type="button" class="on" data-cat="">全部</button><button type="button" data-cat="family">家庭</button><button type="button" data-cat="exp">经验</button></span>
<button type="button" class="gch-more" id="gchMore">去逛逛<i aria-hidden="true">›</i></button>
</div>
<div class="gch-who"><span class="gch-avs" id="gchAvs" aria-hidden="true"></span><span id="gchCnt"></span></div>
<div class="gch-row" id="gchRow"></div>
<style>
#homeBody>section.gc-home{--gt:255,120,150}
.gc-home{margin:16px -26px 0;padding:16px 26px 16px;border-radius:26px;position:relative;z-index:0;background-color:#fff}
.gc-home.hide{display:none}
.gch-head{display:flex;align-items:center;gap:8px}
.gch-title{flex:none;margin:0;font-size:21px;font-weight:900;letter-spacing:.5px;color:#2A1E1E}
.gch-title span{background:linear-gradient(transparent 58%,#FFD0DC 58%,#FFD0DC 92%,transparent 92%);padding:0 3px}
.gch-cat{display:flex;padding:2px;border-radius:99px;background:rgba(240,222,214,.7)}
.gch-cat button{height:22px;padding:0 8px;border:0;border-radius:99px;background:none;font:inherit;font-size:11.5px;font-weight:700;color:#9A7F78}
.gch-cat button.on{background:#fff;color:#D9466A;box-shadow:0 1px 4px rgba(190,110,120,.18)}
.gch-more{margin-left:auto;flex:none;display:inline-flex;align-items:center;gap:2px;height:26px;padding:0 4px 0 10px;border:0;border-radius:99px;background:linear-gradient(135deg,#FF8A9E,#F2627E);font:inherit;font-size:12px;font-weight:800;color:#fff;box-shadow:0 4px 10px rgba(242,98,126,.28)}
.gch-more i{font-style:normal;font-size:15px;line-height:1;padding:0 4px 1px 1px}
.gch-who{display:flex;align-items:center;gap:7px;margin:9px 0 0;font-size:12px;color:#8E7773}
.gch-who b{color:#D9466A;font-weight:800;font-variant-numeric:tabular-nums}
.gch-avs{display:flex}
.gch-avs img{width:20px;height:20px;border-radius:50%;object-fit:cover;border:1.5px solid #fff;margin-left:-6px;background:#FFE3D3}
.gch-avs img:first-child{margin-left:0}
.gch-row{display:flex;gap:10px;margin:12px -26px 0;padding:2px 26px 8px;overflow-x:auto;overflow-y:hidden;scroll-snap-type:x proximity;scroll-padding:0 26px;-webkit-overflow-scrolling:touch;overscroll-behavior-x:contain}
.gch-row::-webkit-scrollbar{display:none}
.gch-row::after{content:"";flex:none;width:16px}
.gch-card{position:relative;flex:none;width:124px;height:152px;padding:12px 11px 10px;border:0;border-radius:18px;overflow:hidden;text-align:left;font:inherit;display:flex;flex-direction:column;scroll-snap-align:start;
background:linear-gradient(155deg,var(--c1),var(--c2));color:var(--ink);box-shadow:0 8px 16px -10px var(--ink),inset 0 1px 0 rgba(255,255,255,.7)}
.gch-card:active{transform:scale(.97)}
.gch-card .gk{align-self:flex-start;height:18px;padding:0 6px;border-radius:6px;background:rgba(255,255,255,.62);font-size:10px;font-weight:800;line-height:18px;letter-spacing:.5px}
.gch-card .gg{position:absolute;right:-8px;top:18px;font-size:72px;font-weight:900;line-height:1;color:#fff;opacity:.55;pointer-events:none;font-family:"STKaiti","KaiTi","Kaiti SC",serif}
.gch-card .gb{position:absolute;right:-26px;bottom:-30px;width:86px;height:86px;border-radius:50%;background:rgba(255,255,255,.28);pointer-events:none}
.gch-card .gn{position:relative;margin-top:auto;font-size:15px;font-weight:900;line-height:1.3;letter-spacing:.2px;color:#2F2424;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.gch-card .gm{position:relative;margin-top:5px;font-size:10.5px;font-weight:600;white-space:nowrap;opacity:.85}
.gch-card .gh{position:absolute;right:0;top:0;height:20px;padding:0 7px 0 8px;border-radius:0 18px 0 10px;background:linear-gradient(135deg,#FF7A45,#F4433C);color:#fff;font-size:10.5px;font-weight:800;line-height:20px;letter-spacing:.5px}
.gch-card.all{align-items:center;justify-content:center;gap:8px;text-align:center;--c1:#FFF6F2;--c2:#FFE9E1;--ink:#C9886F;border:1.5px dashed #F2C9BA;box-shadow:none}
.gch-card.all .ga{width:40px;height:40px;border-radius:50%;display:grid;place-items:center;background:#fff;color:#E2553A;font-size:22px;font-weight:700;box-shadow:0 4px 10px rgba(226,85,58,.15)}
.gch-card.all b{font-size:13.5px;font-weight:800;color:#8A5A4C}
.gch-card.all small{font-size:11px;color:#B39184}
html.night .gch-title{color:#F1E6EE}
html.night .gch-cat{background:rgba(255,255,255,.08)}
</style>
</section>
<!-- 孕期小知识(2026-09-30)。用户原话:「按她填写的孕周,比如孕 20 周 5 天,给她列出几个小模块,
设计一定要漂亮、匹配母婴用户,最好是动态的;小知识匹配每周的每一天」。
内容:assets/data/preg-tips.json(孕 4–40 周,每周 宝宝/妈妈/饮食/注意 四句 + 每天一条小知识,共 7 条),
@@ -2338,6 +2393,7 @@
<script src="/_bridge/avatar-picker.js?v=26101105"></script>
<script src="/_bridge/hand-icons.js?v=26101202"></script>
<script src="js/zodiac-art.js"></script>
<script src="guangchang/topics.js?v=26110109"></script>
<script src="js/app.js"></script>
<!-- 2026-10-03 首页彩蛋:宝宝在肚皮底下动(独立文件,删掉这一行即整体下线;开关在「隐私权限」) -->
<script src="js/bump.js"></script>
@@ -2346,7 +2402,7 @@
<!-- 2026-10-05 首页头图:宝宝时不时动一动小手小脚(独立文件,删掉这一行即下线) -->
<script src="js/baby-limbs.js?v=26101201"></script>
<script src="js/coach.js?v=26101211"></script>
<script src="js/birth-flow.js?v=26110108"></script>
<script src="js/birth-flow.js?v=26110109"></script>
<!-- 禁止下拉回弹(2026-10-11 统一规则,与孕期体重同一段):竖向拖动时从手指下往上找还能朝该方向滚的容器,找不到才拦;横滑、双指、可滚列表不受影响 -->
<script>

View File

@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26110108';
var VER = '26110109';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -4181,6 +4181,52 @@
const avaSrc = n => /^(mom|baby)-\d+$/.test(n) ? 'https://mp.yizhezhe.com/_bridge/avatars/' + n + '.png' : 'assets/img/mom/' + n + '.jpg';
const avaImg = n => '<img src="' + avaSrc(n) + '" alt="" loading="lazy" decoding="async" width="48" height="48">';
const strSeed = t => { let x = 7; for (let i = 0; i < t.length; i++) x = (x * 31 + t.charCodeAt(i)) | 0; return Math.abs(x); };
/* ================= 孕妈广场 · 首页入口(2026-11-01 第一版,占位内容) =================
用户:「在此刻下边加一个广场,放一些话题……不要像『其他妈妈也在搜』那种列表形式」
→ 一排横滑话题封面卡 + 家庭/经验小分段;话题表 window.GC_TOPICS(guangchang/topics.js,与广场页共用)。
进广场是**本站子页**(不是别的 H5 应用),所以不走 jumpApp / 不报 last_app:
相对路径 + 原样带上 location.hash(身份在 hash 里),换页前照例 reportHostLocation。 */
(function initPlaza() {
const sec = $('#gcHome'), row = $('#gchRow');
const T = window.GC_TOPICS;
if (!sec || !row || !Array.isArray(T) || !T.length) return;
const esc = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[c]);
const kfmt = n => n >= 10000 ? (n / 10000).toFixed(1).replace(/\.0$/, '') + '万' : n >= 1000 ? (n / 1000).toFixed(1).replace(/\.0$/, '') + 'k' : String(n);
function go(id) {
haptic();
const url = 'guangchang/' + (id ? '?t=' + encodeURIComponent(id) : '') + location.hash;
reportHostLocation(new URL(url, location.href).href);
location.href = url;
}
function paint(cat) {
const list = T.filter(t => !cat || t.cat === cat);
row.innerHTML = list.map(t =>
'<button type="button" class="gch-card" data-t="' + esc(t.id) + '" style="--c1:' + t.c[0] + ';--c2:' + t.c[1] + ';--ink:' + t.ink + '">' +
'<i class="gb" aria-hidden="true"></i><span class="gg" aria-hidden="true">' + esc(t.g) + '</span>' +
(t.hot ? '<span class="gh">热</span>' : '') +
'<span class="gk">' + (t.cat === 'family' ? '家庭' : '经验') + '</span>' +
'<span class="gn">' + esc(t.name) + '</span>' +
'<span class="gm">' + kfmt(t.n) + ' 篇 · ' + kfmt(t.talk) + ' 人在聊</span></button>'
).join('') +
'<button type="button" class="gch-card all" data-t=""><span class="ga" aria-hidden="true">›</span><b>全部话题</b><small>' + T.length + ' 个话题</small></button>';
row.scrollLeft = 0;
}
paint('');
const talk = T.reduce((a, t) => a + (t.talk || 0), 0);
$('#gchCnt').innerHTML = '<b>' + fmtNum(talk) + '</b> 位妈妈正在聊';
$('#gchAvs').innerHTML = ['p05', 'm07', 's03', 'k09', 'm16'].map(n => '<img src="' + avaSrc(n) + '" alt="" loading="lazy">').join('');
sec.classList.remove('hide');
row.addEventListener('click', e => { const b = e.target.closest('.gch-card'); if (b) go(b.dataset.t); });
$('#gchMore').addEventListener('click', () => go(''));
$('#gchCat').addEventListener('click', e => {
const b = e.target.closest('button[data-cat]');
if (!b || b.classList.contains('on')) return;
$$('#gchCat button').forEach(x => x.classList.toggle('on', x === b));
haptic();
paint(b.dataset.cat);
});
function fmtNum(n) { return String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ','); }
})();
/* 「此刻」重绘钩子:足迹 / 档案 / 盲盒状态回来时调用(它们决定「和你比」「今日待办」和孕周) */
let plHook = null;
const fmtN = n => String(Math.round(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
@@ -4759,7 +4805,7 @@
esc(what).replace(/(\d+)/g, '<em>$1</em>') + '</span>' +
(go ? '<i class="pr-go" aria-hidden="true">›</i>' : '') + '</div>';
}
const FEED_N = 3; /* 常驻三行 */
const FEED_N = 2; /* 常驻两行(2026-11-01 用户:三行轮播改两行;高度见 index.html .pl-feed3 覆盖) */
const ROW_H = 30; /* = .pl-feed3 .pl-row 高度 */
function tick() {
const tr = $('#plTrack');