Files
mama-box/index.html

2045 lines
160 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">
<meta name="theme-color" content="#F4593C">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>兜爸心选 · 孕育陪伴</title>
<!-- V:26100427 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<link rel="stylesheet" href="mama-box.css">
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<!-- 共享助手:授权/跳商城/位置上报(分享卡片同步,见 docs/h5-auth-standard.md 第四节) -->
<script src="js/wx-auth.js"></script>
</head>
<body>
<a class="skip" href="#screens">跳到内容</a>
<div class="phone" id="phone">
<!-- 身份未就绪提示(2026-09-20 新增)
⚠️ 数据源是「宿主有没有把 openid 注入进来」,由 app.js 的 showIdNotice()/hideIdNotice() 控制。
背景:小程序首页壳在拿不到 openid 时会**匿名加载**本页(见 js/app.js 的访客态分支),
此时页面看着能用、其实全是占位(孕周 `--`、查不到任何档案)。以前是静默的,
用户/测试者看不出异常 —— 从商城返回首页加载本页时尤其容易被误判成「数据丢了」。
所以这里给一条**可见**的提示,并提供重试;排查时也能一眼看出是身份问题而不是数据问题。 -->
<div class="id-notice hide" id="idNotice" role="status">
<span class="idn-ico" aria-hidden="true">!</span>
<span class="idn-txt" id="idNoticeTxt">未获取到微信身份,页面数据可能不完整</span>
<button class="idn-retry" id="idNoticeRetry">重试</button>
</div>
<!-- 2026-10-02 底部导航「推荐版」:内容滑到导航下面时淡出,而不是被白条硬切 -->
<div class="tab-fade" aria-hidden="true"></div>
<nav class="tabbar" id="tabbar">
<button class="tab is-active" data-tab="home">
<span class="t-ico t-line"><svg class="t-svg" viewBox="0 0 24 24" aria-hidden="true"><path class="tf" d="M4 10.4 12 4l8 6.4V18a2.4 2.4 0 0 1-2.4 2.4H6.4A2.4 2.4 0 0 1 4 18z"/><text class="td t-dou" x="12" y="17.4" text-anchor="middle">兜</text></svg></span><span class="t-txt">首页</span>
</button>
<button class="tab" data-tab="archive">
<span class="t-ico t-line"><svg class="t-svg" viewBox="0 0 24 24" aria-hidden="true"><path class="tf" d="M8.3 20.4c-1.9 0-2.9-1.5-2.6-3.4l.5-3c.3-1.9 1.4-3 2.9-3 1.6 0 2.5 1.3 2.2 3.2l-.5 3.1c-.3 1.9-1.1 3.1-2.5 3.1z"/><path class="tf" d="M15.9 14.4c-1.4 0-2.2-1.2-2.5-3.1l-.5-3.1c-.3-1.9.6-3.2 2.2-3.2 1.5 0 2.6 1.1 2.9 3l.5 3c.3 1.9-.7 3.4-2.6 3.4z"/><circle class="td" cx="6.6" cy="8.4" r=".95"/><circle class="td" cx="8.6" cy="7.6" r=".85"/><circle class="td" cx="10.4" cy="8.3" r=".75"/><circle class="td" cx="13.6" cy="2.9" r=".8"/><circle class="td" cx="15.5" cy="2.3" r=".85"/><circle class="td" cx="17.5" cy="3" r=".95"/></svg></span><span class="t-txt">档案</span>
</button>
<button class="tab tab-fab" data-sheet="open">
<span class="fab"><img src="assets/img/69818ea975da.png" alt="" aria-hidden="true"><b class="fab-w" aria-hidden="true"><i>记</i><em>一下</em><s>!</s></b></span><span class="t-txt">记一下</span>
</button>
<button class="tab" id="tabLetter">
<!-- ⚠️ 2026-09-19(9.19 需求第 2 条):这枚 tab 从「兜爸心选」商城入口改回「宝宝来信」。
2026-09-20:内置来信屏(#screen-letter)及其全部功能代码已删除 ——
来信功能整体由第三方 H5 承接,这枚 tab 是进「宝宝来信」的入口,
落点 = **第三方「宝宝来信」H5**(mp.yizhezhe.com/laixin/)。
⚠️ 因此依旧**不挂 data-tab**:它不参与 go() 的 tab 高亮切换。
点了走 jumpApp()(同页 location.href + query 带 openid/unionid),
与其它 H5 应用(宫格、工具卡)完全一致 —— 见 app.js 的 #tabLetter 绑定。
⚠️ 红点 #tabBadge 也挂在这枚 tab 上,数据源是 laixin 的
/api/mp/public/laixin/unread,owner 只有 syncLaixinReminder() 一处
(第九批曾把红点挪到「首页」,现已挪回)。 -->
<span class="t-ico t-line"><svg class="t-svg" viewBox="0 0 24 24" aria-hidden="true"><rect class="tf" x="3.4" y="5.6" width="17.2" height="13" rx="2.6"/><path class="tl" d="M4 7.2l8 5.6 8-5.6"/><path class="td" d="M12 15.9s-2-1.2-2-2.5a1 1 0 0 1 2-.3 1 1 0 0 1 2 .3c0 1.3-2 2.5-2 2.5z"/></svg></span><span class="t-badge" id="tabBadge"></span><span class="t-txt">宝宝来信</span>
</button>
<button class="tab" data-tab="me">
<span class="t-ico t-line"><svg class="t-svg" viewBox="0 0 24 24" aria-hidden="true"><circle class="tf" cx="12" cy="8.6" r="3.9"/><path class="tf" d="M4.8 20.2c.6-3.7 3.5-5.9 7.2-5.9s6.6 2.2 7.2 5.9z"/></svg><img class="t-ava" id="tabAva" alt=""></span><span class="t-txt">我的</span>
</button></nav>
<main class="screens" id="screens">
<section class="screen is-active" id="screen-home" data-screen="home">
<div class="home-hero no-letter">
<!-- 2026-10-04 头图改版(用户:宝宝按孕周换形态、孕囊跟着宝宝大小变、去掉飘落的花瓣、加阳光和水波纹):
背景换成「去掉羊水囊的空背景」,羊水囊 + 宝宝整体都在 .hero-baby 里,按孕周 9 个阶段换图(js/hero-fx.js)。
每阶段一张会动的 GIF(本地程序化形变:呼吸、轻浮、小手、蹬脚;脸不动,所以永远闭眼闭嘴)+ 一张同帧静图打底,
静图先出、GIF 加载完再盖上。孕囊大小 = .hero-baby 的 --k,由 JS 按阶段写。
.hero-fx 是阳光光束 + 水波焦散的画布,叠在宝宝之上、文字之下。 -->
<!-- 2026-10-04 七版:#heroWater = 参考页(YANG-bak)的 WebGL 羊水 + 光束 + 呼吸暖光,在宝宝下面;孕囊图透明底叠在上面 -->
<canvas class="hero-water" id="heroWater" aria-hidden="true"></canvas>
<div class="hero-baby" id="heroBaby">
<img class="hero-kid hero-kid-still" id="heroStill" alt="" decoding="async">
<img class="hero-kid" id="heroKid" alt="" decoding="async">
</div>
<!-- 右上角原来有个消息圆钮,点开只弹「暂无新消息」——一个永远没有内容的入口,
还顶着一颗红点天天提示。整个删掉;这一排现在只剩左边的品牌字。
.hero-top 本身留着:它的 height:32 + margin-top:10 撑着下面整列的起点
(现在这一排里除了品牌字还并排放着 slogan,见下面那段注释)。 -->
<!-- slogan 从「品牌字底下另起两行」挪到了「品牌字右边、同一行」。
⚠️ 它现在是 .hero-top 的**孩子**,而 .hero-top 是四个页面共用的
display:flex + justify-content:space-between —— 第二个孩子会被
space-between 甩到最右边去。这里**不动父级的对齐方式**
(动了会连着挪「我的 / 档案」两页右上角的按钮组),
改用 .hero-slogan{margin-right:auto} 把富余空间吃掉,让它贴着品牌字排。
这跟 .hero-actions{margin-left:auto} 是同一个路子,全站一致。
⚠️ 一行 10 个字放得下,是**先把气泡挪下去**换来的,不是本来就放得下。
320 宽(本版支持的最窄屏,CSS 里 max-width:359px 那条断点为证)横向账:
左内边距 16 + 品牌字 4×21 + 4×.5 = 86 → 品牌字排到 x=102
slogan 起点 102 + margin-left 9 → x=111
slogan 宽 10×13 + 9×.3 = 132.7 → 排到 x≈243.7
橙区右内边距 15,可用到 x=305 → 还余 61px ✓
真正卡住的是纵向:slogan 的行盒是 y 18..34(13px 字塞在 16px 行高里,
居中于 y10..42 那一排),而气泡原来 top:26 —— 圆顶正好戳在字底下。
量过 hero-art.png 的 alpha 通道:这幅画是**实心整圆、边缘是硬的**,
α≥8 和 α≥220 量出来的左缘只差 1px,没有可以压上去的透明薄边。
所以只能把气泡整体下移到 top:40,给字底留 6px。
气泡一动,橙区那笔竖列账要连着重算 —— 已经算过,见 style.css 的 .home-hero。
改字号 / 加字 / 挪气泡之前,把上面两笔账都重算一遍。 -->
<div class="hero-top">
<!-- 2026-10-01 左上角文字换成正式 LOGO(用户提供的红字原稿)。头图是橙红底,红色原稿压上去会糊,
所以抠成白色透明 PNG(按红色浓度转 alpha,3x 高清),和旁边白字 slogan、宝宝插画同一套白色语言。 -->
<h1 class="hero-brand"><img class="hero-logo" src="assets/img/db2f99232b39.png" alt="兜爸心选" width="107" height="30"></h1>
<p class="hero-slogan">全家都爱用的孕育平台</p>
</div>
<!-- 2026-10-01 头部左侧改版(用户:「LOGO 下边、小孩左边那块太单一,好好设计一下」)。
原来是「孕周圆环 + 一张天数玻璃卡」,信息只有两条、形状又都是圆/方块。现在四层,从情绪到数据:
① .hi-due 见面倒计时小胶囊(♥ 跳动)—— 最有期待感的一句放最上面
② .hi-week 超大孕周数字「31 周 +6天」—— 主角,替代圆环
③ .hi-bar 孕程进度条,进度头上骑着「本周水果」(宝宝大小),右边写「像菠萝」
④ .hd-card 原来的玻璃卡,「小名在妈妈肚子里 / 已经生活了 N 天」原样保留
竖向账(橙区 250 不变):hero-top 止于 42 → ①顶 54 高 22 → ②顶 82 高 50 → ③顶 140 高 16 → ④顶 166 高 52 → 底 218,
离「宝宝来信」卡顶 234 留 16px(和旧版一样)。宽度不超过 x≈197(旧玻璃卡的右缘),不伸进宝宝插画。
JS 只认 id:heroLeft / heroWeek / heroWeekU / heroPlus / heroBar / heroFruit / heroSize / heroWho / heroDays。 -->
<div class="hero-pane">
<span class="hi-due" id="heroDue"><svg class="hi-heart" viewBox="0 0 24 24" width="11" height="11" aria-hidden="true"><path d="M12 20.4S3.6 14.9 3.6 9.3a4.6 4.6 0 0 1 8.4-2.6 4.6 4.6 0 0 1 8.4 2.6c0 5.6-8.4 11.1-8.4 11.1z" fill="#fff"/></svg><span id="heroDueT">距离见面还有 <b id="heroLeft">--</b> 天</span></span>
<div class="hi-week"><b id="heroWeek">--</b><span class="hi-unit"><em id="heroWeekU">周</em><span id="heroPlus"></span></span></div>
<div class="hi-prog">
<div class="hi-bar"><i class="hi-fill" id="heroBar"><span class="hi-knob" id="heroFruit" aria-hidden="true"></span></i></div>
<span class="hi-size" id="heroSize"></span>
</div>
<div class="hd-card">
<span class="hd-who">
<b id="heroWho">宝宝</b><span class="hd-where">在妈妈肚子里</span>
</span>
<span class="hd-tail">已经生活了 <b id="heroDays">--</b> 天</span>
</div>
</div>
</div>
<!-- 宝宝来信提醒卡。**数据源是 laixin(第三方「宝宝来信」应用)**,
由 js 里的 syncLaixinReminder()(拉数)→ renderLetterReminder()(渲染)唯一负责显隐与文案
(拉 /api/mp/public/laixin/unread;**未建档一律不提示**,建档后 unread > 0 才显示,
卡与底 tab 红点由同一次判定同时落 —— 2026-09-22 产品口径)。
点击 = 跳 laixin,与底 tab「宝宝来信」同落点(app.js 里绑的是 openLetterApp)。
⚠️ 2026-09-20:这张卡**默认就是收起的**(标记里直接带 .hide,
#homeBody 直接带 .no-letter)。卡的显隐要等未读接口回来才知道,
而首屏渲染远早于它 —— 默认显示的话会「先冒出一张假卡、数据回来再缩回去」
(商城↔首页来回切时特别明显)。
⚠️ 别把这里的 .hide / .no-letter 去掉,去掉就退回闪烁版。
⚠️ 这张卡 2026-09-20 之前读的是 box_app 自己的 /mama-box/letters(多数用户 0 封),
而底 tab 早已跳 laixin —— 两套数据导致「红点说有新信、卡里什么都没有」。
改数据源就是修这个,**别再把它接回本地信件数据**(内置来信屏已整体删除,这张卡只认 laixin)。 -->
<button class="letter-card ripple hide" id="homeLetter">
<span class="lc-envwrap"><svg class="lc-env lc-env-v" viewBox="0 0 64 48" aria-hidden="true"><rect x="2" y="4" width="60" height="42" rx="7" fill="#fff"/><path d="M4 9l28 20L60 9" fill="none" stroke="#F7C6D1" stroke-width="2.4" stroke-linejoin="round"/><path d="M4 44l20-17M60 44L40 27" stroke="#FBE1E7" stroke-width="2"/><circle cx="32" cy="29" r="8.5" fill="#E2456A"/><path d="M32 33.2c-2.6-1.6-4-3-4-4.4a2 2 0 0 1 4-.7 2 2 0 0 1 4 .7c0 1.4-1.4 2.8-4 4.4z" fill="#fff"/></svg><b class="lc-num" id="lcNum" aria-hidden="true"></b></span>
<span class="lc-stamp" aria-hidden="true"><i>♥</i></span>
<div class="lc-body">
<!-- ⚠️ 这段只是**兜底**(正常情况下用户看不到:卡默认 .hide,
有未读时由 renderLetterReminder() 改写成「宝宝来信」+ 通用提醒文案)。
改这几行时连着那边一起看,否则会出现「HTML 里写着一套、屏幕上是另一套」。
⚠️ 上一批这里短暂叫过「宝宝的新消息」,本批随整条线退回「来信」。 -->
<div class="lc-title" id="lcTitle">宝宝来信</div>
<div class="lc-desc" id="lcDesc">妈妈,今天我在你肚子里翻了个身……</div>
<div class="lc-link"><span id="lcLink">读读宝宝的话</span> <i>›</i></div>
</div>
<span class="lc-arrow" aria-hidden="true"><svg viewBox="0 0 24 24" width="16" height="16"><path d="M9 5l7 7-7 7" fill="none" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
</button>
<!-- 来信卡收起来时由 hideLetterCard() / renderLetterReminder() 给 #homeBody 挂 .no-letter,
把下面第一块(.tool-dock)的上间距补回来。
⚠️ 2026-09-20:它**默认就带 .no-letter**(与 #homeLetter 默认带 .hide 配套,
首屏按「没有来信卡」排版,避免数据回来时布局跳一下)。
── 这一版首页正文换了骨架 ──
上一版是同一套「竖色条标题 + 灰色说明 + 一块内容」原样重复四遍
(工具 / AI 小结 / 脚印 / 指南),四处的字号、间距、列宽全一样,
只有色条颜色不同,往下扫就是「条-块-条-块-条-块-条-块」。
现在那套框架整个拆了:.sec-title / .sec-sub 在首页一处不留,
抬头改为长在各模块**内部**,四个模块收成三块,
靠三种完全不同的容器语言自己分段 ——
.tool-dock 通栏出血的浅米托盘
.today-line 没有容器,只有一条从早贯到晚的竖线
.shelf 横向可滑的书架卡带
页面也因此有了两种宽度节奏:文字型内容仍在 26px 栏内,
托盘和卡带出血到屏幕边缘。 -->
<div class="home-body no-letter" id="homeBody">
<!-- 模块一 · 工具托盘。
抬头进了托盘里面:标题独占一行,右端「所有工具」做成白底细边胶囊;
说明单独做成一枚浅色 chip,不再是标题底下一行贴边的灰字。
通往「记一下」弹层的门这一屏仍是两处,是故意的,不是漏删:
这颗胶囊是**导航**位(扫抬头找"看全部"时手先落在这儿),
宫格右下角那张 .mini-card.more 是**内容**位(顺带把第三列剩下的
44px 填平,而且是个够手指点的大靶子)。两个都指向 data-sheet="open",
删掉任意一个另一个都还走得通。 -->
<section class="tool-dock">
<!-- 大字水印。aria-hidden:读屏念到这块该直接进标题,
不该先听见两声「兜爸」。样式和理由都在 css/style.css 的 .wm 上。
2026-10-01 用户要求:水印字从「妈妈」改成品牌名「兜爸」。 -->
<span class="wm" aria-hidden="true">兜爸</span>
<div class="dock-head">
<h2 class="dock-title" id="dockTitle">宝宝的孕育工具</h2>
<button class="dock-all" data-sheet="open">所有工具 ›</button>
</div>
<p class="dock-note"><svg viewBox="0 0 24 24" width="11" height="11" aria-hidden="true"><path d="M11 2.6l1.9 5.2 5.2 1.9-5.2 1.9L11 16.8l-1.9-5.2L3.9 9.7l5.2-1.9L11 2.6z" fill="currentColor"/><path d="M18.4 14.6l.8 2.1 2.2.8-2.2.8-.8 2.1-.8-2.1-2.2-.8 2.2-.8.8-2.1z" fill="currentColor" opacity=".6"/></svg>妈妈每记录一次,宝宝就多一段成长记忆</p>
<!-- 2026-09-30 五张卡改版:白色粗标题 + 各工具 LOGO 里的图形(取自点进去那个工具页左上角的 LOGO,
只留里面的图形),做成白色单色半透明「水印」斜压在卡片右下角,只作装饰;assets/img/*.svg 矢量。卡底色跟点进去的工具页同色系、稍深一档。
只换皮:排布、高度账、data-mini 跳转和各 id 的数据回填一律没动。样式见 mama-box.css「参考图改版」。 -->
<!-- 五张工具卡 + 一个出口。宫格本身这一版没动:
不等宽三列 + 竖向堆叠本来就是这一屏最不传统的部分,
而且两列高度是硬凑齐的(见 style.css 里 .mini-card.curve 上面那段),
动它得整列重算,收益也不在这儿。 -->
<div class="bento">
<div class="bento-col">
<button class="mini-card kick ripple" data-mini="kick">
<div class="mc-head">
<div class="mc-title">数胎动</div>
<!-- ⚠️ 默认值必须是中性占位,**不能写演示数字**:访客态(无 openid)与
records 接口失败/超时这两种情况下没人会改它,写死 3 就是假数据。
真实值由 __applyRecords() 写入(未记录的新用户是 0)。
(2026-09-22 新用户实测:工具卡上的演示占位值一律归中性。) -->
<div class="mc-sub">今天 <b id="kickTodayHome">--</b> 次</div>
<!-- 第二行(上游 sub,如「2 段 · 最近 13:00」)——只有 kick 与 weight 两张卡
有富余竖向空间;fweight/cal/curve 是固定高度且列很窄,放不下第二行。 -->
<div class="mc-extra" id="kickExtra"></div>
</div>
<span class="mc-btn">记录</span>
<img class="mc-art" src="assets/img/6add0c9e5856.svg" alt="" aria-hidden="true">
</button>
<!-- 第五个工具。原来是「胎心记录」,本批换成「胎儿估重」——
后者是真正部署好的站点(beian1.douba.top),点了能用;
而胎心记录在这张卡上一直只是个弹 toast 的占位入口。
⚠️ 胎心记录**没有被删掉**:它还在「记一下」弹层里(快捷区 q3 +
全部工具宫格),档案页那两条「来自「胎心记录」」的历史记录也照旧。
这里换掉的只是首页宫格的一个坑位。
⚠️ 类名、结构、三个尺寸全部照搬原来那张卡:外层仍是 .mini-card
(.heart 只是改名成 .fweight,两条 CSS 跟着改),内部仍是
「名字 + 状态两行叠着 + 右下角插画」。所以左列 158 + 10 + 64 = 232
这笔账一个数都没动,右列(88 + 10 + 80 + 10 + 44)不用跟着补。
图标换成了内联 SVG 的表盘,理由见 patch 脚本里那段注释。 -->
<button class="mini-card fweight ripple" data-mini="fweight">
<div class="mc-title sm">胎儿估重</div>
<div class="mc-sub">去测算</div>
<img class="mc-art" src="assets/img/3c4cf0da356c.svg" alt="" aria-hidden="true">
</button>
</div>
<button class="mini-card weight ripple" data-mini="weight">
<img class="mc-art big" src="assets/img/fc0ab3d2e21a.svg" alt="" aria-hidden="true">
<div class="mc-title">孕期体重</div>
<!-- ⚠️ 这里原来是写死的演示值「58.2 kg」,任何用户都会看到(2026-09-22 演示数据泄漏)。
现在由 `applyRemoteCards()` 按 box_app 封装的卡片接口写入:
有值时写 `value` + 小字 `<small>unit</small>`,没有值时保持中性占位 `--`
(**不写 0**,0 是个看着像真的假值)。接口拿不到就保留这里的默认值。 -->
<div class="mc-big" id="weightVal">--</div>
<div class="mc-extra" id="weightExtra"></div>
</button>
<div class="bento-col">
<button class="mini-card cal ripple" data-mini="cal">
<div class="mc-title sm">孕检日历</div>
<div class="mc-sub" id="calNext">—</div>
<img class="mc-art" src="assets/img/a693b92f3239.svg" alt="" aria-hidden="true">
</button>
<!-- 生长曲线原来是 flex:1,把这一列剩下的高度全吃了(约 134px),
而它的内容和上面的孕检日历一模一样多。压到 80px,
省下的 54px 给下面那张「更多工具」。
⚠️ 本批(第九批)把这张卡从「生长曲线」换成「数宫缩」:
文案改这一行;落点改 app.js 的 MINI_APP_MAP.curve
(taiershengzhang → gongsuopinlv,指向已上架的「宫缩频率计算」)。
⚠️「生长曲线」工具本身**没被删**:App 表里 taiershengzhang /
taierquxian 都还在,仍会出现在「记一下」的全部工具列表里;
这里换掉的只是首页这一个坑位。
图标(2026-09-30)已换成秒表萌物,见下面五张卡的改版注释。
⚠️ 副标题仍是 data-wk="cn" 自动显示孕周,本批未要求改文案。 -->
<button class="mini-card curve ripple" data-mini="curve">
<div class="mc-title sm">数宫缩</div>
<div class="mc-sub" data-wk="cn">—</div>
<img class="mc-art" src="assets/img/ad7487f2e445.svg" alt="" aria-hidden="true">
</button>
<!-- 通往「记一下」弹层里那十几个工具。底部那颗 FAB 开的也是它。
故意不做成糖果色卡:它不是第六个工具,是个出口,
灰米底 + 一行小字,和上面五张分得开。 -->
<button class="mini-card more ripple" data-sheet="open">
<span class="mc-more-t">更多工具</span><i class="chev">›</i>
</button>
</div>
</div>
<!-- 此刻(2026-10-01 需求文档 v1.0 第二节;同日两次改版)。数据 GET /api/mp/public/mama-box/pulse,渲染在 app.js pulseCard()。
⚠️ 模拟数据是产品负责人 2026-10-01 拍板的例外(AGENTS.md §4.12),只限这一条。
用户 2026-10-01:「把此刻跟上面的孕育工具结合到一起……此刻相当于上面那个卡片的一个轻量化的展示」
→ 不再单独成卡,收进工具卡底部一条浅色带:一行人数 + 她们在做 / 和你比 + 今天别忘了 + 一行滚动动态。
「她们在聊」按用户要求删除;全站版(访客)的「今日三项」小卡也去掉(和上面工具宫格重复)。
show=false 或接口失败 → 整条保持 .hide,工具卡和以前一样。
同日三改(用户:「此刻有一点点不够清爽,文字太多了,密密麻麻的」):从 5 行减到 3 行 ——
① 标题行不变;② 「她们在做」两条进度条合成**一句**,只挑你今天还没做的那项(都做了就一句夸奖),去掉进度条;
③ 「今天别忘了」整行删(产检、盲盒在上面工具宫格和盲盒横幅里都有);④ 滚动动态去掉白底卡片,压成一行小字。 -->
<!-- 2026-10-02 陪伴版(用户:「改成陪伴的语气……头像展示 6 到 8 个……升级成同周妈妈的悄悄话……
不做复杂社区,只做一点陪伴」):
① 头像从标题行挪到第二行,一排 7 个,后面跟「今天 N 位同周妈妈陪宝宝数了胎动」——说人数不说百分比,不和你比;
② 「你今天还没数 ›」改成邀请:「小米粒在等你,陪它数一数 ›」,做过了就是「你今天也陪它数过啦 ✓」;
③ 同周悄悄话 #whWall:横滑的小纸条,只能「抱抱」不能评论,没有昵称头像。数据 GET /whisper(app.js whisperWall())。
拉到悄悄话时滚动动态 #plFeed 让位(一块地方只讲一件事),拉不到就还是滚动动态。 -->
<div class="pulse hide" id="pulse" role="region" aria-label="此刻,同孕周妈妈们">
<div class="pl-head">
<span class="pl-live"><i></i>此刻</span>
<span class="pl-title" id="plTitle"></span>
</div>
<div class="pp-crowd">
<span class="pp-avs" id="ppAvs" aria-hidden="true"></span>
<span class="pp-col">
<span class="pp-crowd-t" id="ppCrowd"></span>
<span class="pp-doing hide" id="ppDoing"></span>
</span>
</div>
<!-- 2026-10-02 用户:「此刻给我放 3 个使用应用记录」→ 动态从「一行滚动、有悄悄话就让位」改成常驻三行,
排在悄悄话入口上面;每 3 秒最上面一条淡出、底下补一条;点一行直接进对应工具(act → TODAY_GO)。 -->
<div class="pl-feed pl-feed3 hide" id="plFeed"><div class="pl-track" id="plTrack"></div></div>
<!-- 2026-10-02 二改(用户:「此刻有一点点乱……悄悄话点进去,前端一个很简洁、轻量化的入口,
点进去是整个悄悄话的大页面,设计炫酷一点、有创意、视觉有冲击力」):
首页只留一条入口(一句轮播的悄悄话 + 条数),横滑纸条带挪进全屏页 #whPage。 -->
<button class="wh-entry hide" id="whWall" type="button" aria-label="打开同孕周悄悄话">
<!-- 2026-10-02 用户:「入口那一小条里要有星空,星星一闪一闪,再划过几个流星,不要太多」 -->
<span class="we-sky" aria-hidden="true"><i class="tw" style="left:50%;top:12%;width:6px;height:6px;animation-delay:-1.2s;animation-duration:2.4s"></i><i style="left:60%;top:30%;width:4px;height:4px;animation-delay:-0.3s;animation-duration:2.0s"></i><i class="tw" style="left:73%;top:15%;width:5px;height:5px;animation-delay:-2.4s;animation-duration:2.8s"></i><i class="tw" style="left:93%;top:13%;width:4px;height:4px;animation-delay:-3.1s;animation-duration:2.2s"></i><i class="tw" style="left:21%;top:90%;width:4px;height:4px;animation-delay:-3.7s;animation-duration:2.6s"></i><i class="tw" style="left:45%;top:88%;width:5px;height:5px;animation-delay:-1.6s;animation-duration:3.2s"></i><i style="left:73%;top:80%;width:4px;height:4px;animation-delay:-2.2s;animation-duration:2.6s"></i><i class="tw" style="left:89%;top:88%;width:5px;height:5px;animation-delay:-0.8s;animation-duration:3.0s"></i><b class="we-mt"></b><b class="we-mt m2"></b></span>
<span class="we-ico" aria-hidden="true"><svg viewBox="0 0 24 24"><path d="M5 18.5V7.8A2.8 2.8 0 017.8 5h8.4A2.8 2.8 0 0119 7.8v5.4a2.8 2.8 0 01-2.8 2.8H9.2z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/><path d="M9 10.5h6" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/></svg></span>
<span class="we-txt"><b id="weName">孕妈悄悄话</b><span class="we-topic"><em>本周话题</em><strong id="weTopic"></strong></span><span class="we-roll" id="weRoll"></span></span>
<span class="we-go" id="weGo"></span>
</button>
</div>
</section>
<!-- 孕期小知识(2026-09-30)。用户原话:「按她填写的孕周,比如孕 20 周 5 天,给她列出几个小模块,
设计一定要漂亮、匹配母婴用户,最好是动态的;小知识匹配每周的每一天」。
内容:assets/data/preg-tips.json(孕 4–40 周,每周 宝宝/妈妈/饮食/注意 四句 + 每天一条小知识,共 7 条),
首次需要时才 fetch,不进首屏。渲染在 app.js renderTips()。
⚠️ 默认 .hide:只有拿到**真实孕周**(state.pregEmpty === false 且非产后)才显示,
内置默认孕周(DEFAULT_DUE 推出来的 27 周上下)是假的,不能拿它配知识。
⚠️ 一周 7 个圆点 = 这一周的第 1–7 天。今天及以前可以点开回看;以后的几天锁着,
写「明天见」—— 让每天回来看一条,而不是一口气翻完。 -->
<section class="preg-tips hide" id="pregTips">
<!-- 2026-10-01 用户要求:标题前写明孕周 ——「孕 26 周 · 今日孕期小知识」,按她填的孕周自动取,每天换一条;整块压矮一些。
#ptWk 由 renderTips() 写;#ptWhen 改成「第 N 天」。下面四张小卡由 2×2 改成一行横滑,高度省一半。 -->
<div class="pt-head">
<h2 class="pt-title"><span class="pt-wk" id="ptWk">孕 — 周</span>今日孕期小知识</h2>
<span class="pt-when" id="ptWhen"></span>
</div>
<div class="pt-today" id="ptToday">
<span class="pt-float" id="ptFloat" aria-hidden="true"></span>
<span class="pt-badge" id="ptBadge">今日小知识</span>
<div class="pt-body" id="ptBody">
<div class="pt-t" id="ptT"></div>
<div class="pt-d" id="ptD"></div>
</div>
<div class="pt-days" id="ptDays" role="tablist" aria-label="本周每天的小知识"></div>
</div>
<!-- 2026-10-01 用户要求:四张卡不在首页放文字,只留图标 + 标题四个入口,点进详情页 #detailTips 看全文 -->
<div class="pt-chips" id="ptChips">
<button type="button" class="pt-chip pc-baby" data-pt="baby"><i id="ptChipBaby" aria-hidden="true">👶</i><span>宝宝这周</span></button>
<button type="button" class="pt-chip pc-mom" data-pt="mom"><i aria-hidden="true">🤰</i><span>妈妈身体</span></button>
<button type="button" class="pt-chip pc-eat" data-pt="eat"><i aria-hidden="true">🥗</i><span>吃点什么</span></button>
<button type="button" class="pt-chip pc-care" data-pt="care"><i aria-hidden="true">📋</i><span>这周留心</span></button>
</div>
<p class="pt-foot">仅供参考,身体有任何不适请以医生意见为准</p>
</section>
<!-- 3D 婴儿房 · 「透过页面看到后面的房间」(2026-10-01 三版)。用户原话:
「把那个房间整体……铺到它的后边……两个模块中间,它不用点击,从外边其实就能看到这个里边是什么,
整个铺到这个背景的后边,有一个引导进入的按钮,我点进去再弹出这个新的页面」。
做法:这一块没有底色,是页面上开的一道「空隙」;房间图层(#nypLayer)由 JS 每次滚动都对齐到 .screen 的可视区,
所以页面往上滚时房间不跟着走,看起来就是铺在整页背后、从缝里露出来(视差)。上下两条内阴影 = 上下模块压在房间上面。
整块都能点(2026-10-02 用户要求:点到房子 / 模块任意位置都直接进 3D 婴儿房,不必非点「进去布置」);滑动页面不会触发 click,不怕误触。
图:assets/img/nursery-day.jpg / nursery-night.jpg,由「3D 婴儿房」会话(33d6c837)导出的无 UI 渲染
(原图 yef/cover-day.png、cover-night.png,1125×1500,**原尺寸**存 JPG —— 375 宽 × 3 倍屏正好 1125 像素,别再缩,缩到 750 在 iPhone 上会糊(2026-10-01 用户反馈过));里面摆的是演示布置,不是用户自己的房间。
底色必须和图边一致:白天 #DCE5EC、夜间 #2C3350(2026-10-01 写实版,取图顶两角;旧卡通版是 #FFECD8 / #5A5890),否则缝里会露出图片边框。
2026-10-01 晚:3D 婴儿房会话把封面换成「整栋小别墅外景」(婴儿房是二楼剖开那一间),房子占画面中部偏下、上方约 20% 纯底色。
缝高 300→340、图宽 100%→88%(max 420)、上提 40px:缝滚到屏幕中间时屋顶到草坪整栋都露出来,草坪压在玻璃条下面;标题改「我们的家」。
位置:2026-10-01 用户要求和消消乐对调 —— 后又和孕期小知识对调 —— 现在在孕期小知识下面、今天的动线上面(原来在今天的动线和书架之间)。北京时间 19:00–6:00 换夜间图(开灯版)。JS 在 app.js nurseryRender()。 -->
<section class="ny-peek" id="nursery">
<div class="nyp-layer" id="nypLayer" aria-hidden="true"><img id="nypImg" src="assets/img/nursery-day.jpg?v=20261001h" alt="" decoding="async"></div>
<span class="nyp-chip" id="nyChip">进去看看 · 宝宝的婴儿房</span>
<div class="nyp-bar">
<div class="nyp-txt"><b id="nyTitle">我们的家</b><span id="nySub">做孕期任务攒兜豆,一件件布置好</span></div>
<button class="nyp-btn" id="nyBtn" type="button">进去布置 <svg viewBox="0 0 24 24" width="11" height="11" aria-hidden="true"><path d="M8 5l8 7-8 7z" fill="currentColor"/></svg></button>
</div>
</section>
<!-- 首页 · 兜豆兑换商城模块(2026-10-01):紧跟婴儿房(那张卡写着「攒兜豆把它布置好」),
顺着「兜豆」这条线往下接。头部一条 + 下面一排可兑好物;点哪儿都 openExchange()。 -->
<!-- 外层 .exch-wrap 套上和其它模块同款的白色外壳(2026-10-01 用户:「应该有个底色,跟其他模块一样」),里面粉色卡不变 -->
<section class="exch-wrap" id="homeExchMod"><div class="exch-mod">
<button class="exch-strip ripple" id="homeExch">
<span class="es-coin" aria-hidden="true"><img src="assets/img/dou-coin.svg?v=20261002" width="36" height="36" alt=""></span>
<span class="es-main"><b>兜豆兑换实物</b><span id="homeExchSub">做孕期任务就能攒兜豆</span></span>
<span class="es-go">去兑换<i>›</i></span>
</button>
<!-- 本场可兑好物(/mall/drop:有库存且已开售,按 heat_30m 降序取前 6),横向滑动;拉不到数据整行隐藏 -->
<div class="ex-goods hide" id="homeExchGoods"></div>
</div></section>
<!-- 模块二 · 今天的动线。
上一版这里是两个各自独立的模块:「AI今日小结」一张渐变卡,
下面「今日成长脚印」一张白卡装的时间轴。可 AI 那句话总结的
就是脚印里这四条记录,中间隔着一整套抬头分开摆,两边都在自说自话。
现在合成一条线:起点是「今天」,中间四条记录,
末尾挂 AI 那句话当这一天的落款。
时间轴外面那张白卡也撤了 —— 结构改由 .today-line::before 那条
贯穿到底的竖线撑起来,不再靠一个方框圈出来,
这样它和上面的托盘、下面的卡带才真的是三种容器语言。
⚠️ .steps 仍是整卡可点的 role="button",里面**不能**再出现 <button>。
所以「查看全部 ›」挂在 .tl-head 上、AI 气泡是 .steps 的兄弟节点,
两者都在 .steps 外面。 -->
<!-- 今日脚印 2026-10-01 改版(用户:「对标其他模块,UI 要漂亮一些,内容文字也要有创意」):
· 头部左边一张撕页日历(日 / 月·周几),右边标题 + 一句小诗;
· 每条记录前的圆点换成**一左一右交替的小脚丫**,沿虚线往下走 —— 「脚印」两个字画出来;
· 每条记录是一张小卡:大字内容(数字加粗上色)+ 时间 + 来源小标签;
· 没记录时(guide)是一行脚丫插画 + 接口给的引导语(童趣字体)+ 带 logo 的应用芯片;
· AI 小结做成「宝宝写给妈妈的一张便签」:信纸横线 + 宝宝口吻 + 落款小名。
⚠️ 「AI」角标必须保留(唯一的机器生成提示,见 syncBabyWords 那段注释)。
JS 依赖的 id / class(.today-line / #tlSteps / .step 系列 / #tlGuide* / .ai-bubble / .ab-say / #abTag / #tlTitle)都沿用。 -->
<section class="today-line fp2">
<div class="fp-head">
<div class="fp-cal" aria-hidden="true"><i class="fc-ring"></i><b id="fpCalD">--</b><span id="fpCalM">--</span></div>
<div class="fp-ht">
<h2 class="tl-h-title" id="tlTitle">宝宝的今日脚印</h2>
<p class="fp-poem fp-date" id="fpDate"></p>
</div>
<button class="fp-all" data-goto="archive">全部<i>›</i></button>
</div>
<div class="steps fp-steps ripple" id="tlSteps" data-goto="archive" role="button" tabindex="0" aria-label="宝宝的今日脚印,查看完整档案">
<div class="step"><span class="st-time">今天</span><span class="st-dot"></span><span class="st-txt">正在获取今天的脚印…</span><span class="st-from"></span></div>
</div>
<!-- 2026-10-02 用户要求:今天还没用任何工具时,不再放示例/引导语,改成「今日待办」清单,提醒她今天该做什么。
条目 = 接口 unused(今天还没用的、已上架的应用),文案由 app.js FP_TODO 按应用名配成一句动作。 -->
<div class="tl-guide fp-guide fp-todo" id="tlGuide" style="display:none">
<div class="td-head"><b>今日待办</b><span class="td-cnt" id="tdCnt"></span></div>
<p class="tl-guide-t td-lead" id="tlGuideTitle"></p>
<div class="tl-guide-apps td-list" id="tlGuideApps"></div>
<p class="tl-guide-s td-foot" id="tlGuideSub"></p>
</div>
<button class="ai-bubble fp-note ripple" data-open="detailAI">
<span class="fn-top"><b class="ab-tag" id="abTag">宝宝今天想说</b></span>
<span class="ab-say"></span>
<span class="fn-foot"><span class="fn-sign" id="fpSign">—— 宝宝</span><em>往期小结 ›</em></span>
</button>
</section>
<!-- 树洞(2026-09-30)。用户原话:「宝妈们有任何情绪上的问题,都可以在这个树洞里扔进去」。
用户选定:**只存在她自己手机上**(localStorage 'mmbox.treehole.v1',不上传、后台也看不到),
扔进去后**只有「已收好」的动画,不做回应**。
⚠️ 唯一的例外是安全提示:内容里出现明显的轻生字眼时,收好之后在卡内显示一行援助热线
(12356 全国心理援助热线),这不是「回应心事」,是兜底,别删。
⚠️ 纸团飞进树洞的动画靠 Element.animate;不支持的老内核直接跳到「已收好」,不影响收存。 -->
<!-- 「妈妈的树洞」整块已删(2026-10-01 用户要求)。样式与 JS 一并删除,需要恢复看 git 历史 70c9786。 -->
<!-- 宝宝消消乐 · 推广入口(2026-09-30)。用户原话:「做了一个消消乐小游戏(待产清单主题),
放到首页展示并推广,宝妈们业余时间玩,名字叫宝宝消消乐」。
2026-10-01 用户要求和 3D 婴儿房对调位置:现在在「今天的动线」和书架之间(原来在工具托盘正下方)。右侧 3×3 小棋盘是纯装饰动画
(每隔几秒一行三个同样的待产用品闪一下「消掉」再换新),JS 在 app.js gamePromo()。
游戏地址在 app.js 的 GAME_URL;为空时整块隐藏(.hide 默认)。点击走 jumpApp(),带身份跳转。 -->
<section class="game-promo hide" id="gamePromo">
<button class="gp-card ripple" id="gpCard" type="button" aria-label="开始玩宝宝消消乐">
<span class="gp-new">兜爸旗下小游戏</span>
<span class="gp-text">
<span class="gp-name">宝宝消消乐</span>
<span class="gp-sub">三连就消掉,边玩边备待产包</span>
</span>
<span class="gp-board" id="gpBoard" aria-hidden="true"></span>
<span class="gp-cta">开始玩 <svg viewBox="0 0 24 24" width="12" height="12" aria-hidden="true"><path d="M8 5l8 7-8 7z" fill="currentColor"/></svg></span>
<span class="gp-spark s1" aria-hidden="true">✦</span><span class="gp-spark s2" aria-hidden="true">✦</span><span class="gp-spark s3" aria-hidden="true">✦</span>
</button>
</section>
<!-- 模块三 · 书架卡带。
上一版是「一张横排大卡 + 下面两张小卡」的老三段,和上面两个模块
一样都是往下堆。这里改成一条横向可滑的书架:三张等宽等高的**竖版**书
(封面在上、书名在下、免费标在底),右边故意露出下一张的边角,
不用画箭头也知道能往右滑。整页因此多了一段「可以往右读」的节奏 ——
这是最直观的一处排版变化。
资源这类内容对可发现性的要求低(不像工具是每天要点的),
收进横滑里是合适的;工具那一屏就没这么干。
⚠️ data-wk="n" 是从原来那条 h2 里搬过来的。app.js 用
$$('[data-wk]') 全局扫,属性一丢孕周就永远显示「—」。 -->
<section class="shelf gift2">
<!-- 2026-10-01 改版:用户要「资料免费送、是一种福利、针对这个孕周」的感觉。
抬头改成礼盒 + 「孕N周专属福利」,三张书卡改成「福利券」:左上孕周专属角标、
封面右上「免费」印章、中间一道撕券虚线、底行「免费领 ›」。
⚠️ data-wk 照旧(app.js $$('[data-wk]') 全局扫,角标里那两个也会被填上)。 -->
<div class="shelf-bar gb-bar">
<span class="gb-gift" aria-hidden="true"><svg viewBox="0 0 24 24" width="26" height="26"><rect x="4.5" y="10.5" width="15" height="9.5" rx="2" fill="#fff"/><rect x="3.2" y="7.4" width="17.6" height="4.2" rx="1.6" fill="#fff"/><rect x="11" y="7.4" width="2" height="12.6" fill="#F7745E"/><rect x="3.2" y="11" width="17.6" height="1" fill="#F7745E" opacity=".35"/><path d="M12 7.4C10.6 4.6 7.6 4.2 7.6 6.1c0 1.3 2.2 1.3 4.4 1.3zm0 0c1.4-2.8 4.4-3.2 4.4-1.3 0 1.3-2.2 1.3-4.4 1.3z" fill="none" stroke="#fff" stroke-width="1.6" stroke-linejoin="round"/><path d="M20 1.8l.5 1.3 1.3.5-1.3.5-.5 1.3-.5-1.3-1.3-.5 1.3-.5z" fill="#FFF3C4"/></svg></span>
<div class="gb-ht">
<h2 class="sh-title">孕<span data-wk="n">—</span>周专属福利</h2>
<p class="sh-note">这周用得上的孕育资料,<b>全部免费送</b></p>
</div>
<button class="sh-all" data-open="detailResList">全部 ›</button>
</div>
<!-- 三张卡的内部结构是**逐行对齐**的:封面 / 标题 / 简介 / 底行,一个不缺。
上一稿只有第一张有简介、也只有第一张有「立即领取」,另两张少两截,
于是三张卡的标题落在不同高度、底行也不齐 —— 这就是看着乱的原因。
封面给了固定高度(80px + object-fit:contain),底行靠
.sb-row{margin-top:auto} 顶到卡底,中间的简介统一截两行,
三张就自然等高、四段横着都成列了。
⚠️ 原来第一张那颗「立即领取」删掉了:整张卡本身就是个 <button>,
点哪儿都是进详情页,那颗 span 长得像按钮却并不真的领取,是个假承诺;
而且三张卡并列时三颗橙色大按钮也太吵。领取动作留在详情页底部。
底行读作一句话:「这些人领了 → 一共 12.8 万 → 而且免费」。
所以头像贴着人数(同一件事的两半,中间只留 7px),「免费」用
margin-left:auto 甩到最右,是另起的一件事。
.sb-faces 里那 6 个 <i> 是**装饰**,不是真实用户头像 ——
assets/ 里没有人像图,也不该为这点装饰新增二进制资源,
所以整叠头像是纯 CSS 画的(圆底 + ::before 头 + ::after 肩)。
aria-hidden:读屏念到这行应该只听见「12.8 万妈妈已领取」,
而不是六个没有内容的空节点。
⚠️ 上面那三个数(12.8 / 20.4 / 9.6 万)是**真实领取量**,不是编的 ——
2026-09-22 有人按「人气数看着像演示值」把它们连同头像叠一起删了,
属**误判**,已恢复。**别再删**。它们是**手工同步的静态文案**:
接口虽返回同源的 got(已领),但 __applyResources 明确「有意不读」
(9.19 起 H5 已无「已领」概念),所以更新领取量时要**手工改这三处**;
要改成读接口,得先把这个口径捋清再动。 -->
<div class="shelf-rail">
<button class="shelf-book sb-book ripple" data-open="detailRes" data-res="book">
<span class="sb-tag">孕<span data-wk="n">—</span>周专属</span>
<img class="sb-cover" src="assets/img/9f3fa8edf4aa.png" alt="" aria-hidden="true">
<span class="sb-title">《糖耐检查全攻略》</span>
<span class="sb-desc">孕24-28周必看 · 附检查前饮食清单</span>
<span class="sb-row"><span class="sb-faces" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i></span><i class="sb-got">12.8 万妈妈已领取</i><span class="pill-free">免费领</span></span>
</button>
<button class="shelf-book sb-food ripple" data-open="detailRes" data-res="food">
<span class="sb-tag">孕<span data-wk="n">—</span>周专属</span>
<img class="sb-cover" src="assets/img/3657b1f76026.png" alt="" aria-hidden="true">
<span class="sb-title">孕期营养全书</span>
<span class="sb-desc">按孕周编排的一日三餐参考</span>
<span class="sb-row"><span class="sb-faces" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i></span><i class="sb-got">20.4 万妈妈已领取</i><span class="pill-free">免费领</span></span>
</button>
<button class="shelf-book sb-music ripple" data-open="detailRes" data-res="music">
<span class="sb-tag">孕<span data-wk="n">—</span>周专属</span>
<img class="sb-cover" src="assets/img/67405412c52c.png" alt="" aria-hidden="true">
<span class="sb-title">胎教音乐合集</span>
<span class="sb-desc">30 首适合孕中期的舒缓曲目</span>
<span class="sb-row"><span class="sb-faces" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i></span><i class="sb-got">9.6 万妈妈已领取</i><span class="pill-free">免费领</span></span>
</button>
</div>
<!-- ⚠️ 这句话现在是**静态文案**(2026-09-19 9.19 需求:兑换码闸门取消,
不再有「已解锁/未解锁」两版口径,原来那个给 syncUnlockCopy() 用的
#shelfLeadTxt 靶子和该函数一起删了)。
旁边「查看全部 ›」是个 <a data-open>,它的点击监听是 init 时一次性
绑上去的 —— 谁要是图省事对整个 <p> 写 innerHTML,链接会连同监听被静默
换掉(看着一模一样,就是点不动),别再引入这种写法。
⚠️ 128 是**后端准备好的资料总数**,不是「资源列表里当前有多少条」——
2026-09-22 有人按「数字与列表条数不符」把它改成 RES 的条数,属**误判**,已回退:
· 资料按孕周陆续发放,目前上架 8 份,其余会陆续跟进;
· 没上架的**并非拿不到** —— 扫码加小助手可以直接索要(见顾问二维码弹窗)。
所以「128 份等你来领」是邀请口径(含找小助手要的),跟「列表当前 8 条」
是两件事,**数字不一样是对的**,别再"修"。 -->
<p class="unlock"><i class="ul-gift" aria-hidden="true"></i>福利库还有 <b>128</b> 份资料等你领 · <a data-open="detailResList">查看全部 ›</a></p>
</section>
<!-- 孕期盲盒 · 首页细横幅(2026-10-01,需求文档 v1.0 三-7)。
放在顶部大图正下方(需求:顶部大图 → 盲盒横幅 → 孕育工具 → 此刻 → 消消乐)。
文案/状态全由 app.js renderBanner() 按 /blindbox 返回写入;访客态(无 openid)、孕 4 周前、接口失败 → 保持 .hide。
⚠️ 2026-10-02 用户:「把盲盒放到首页下边,给一个位置就行,不用太明显」→
挪到 #homeBody 最后(福利书架之后),样式收成一条低调细条(见 CSS「盲盒挪到页底」)。
工具托盘因此重新成为第一个孩子,.home-body>:first-child 间距直接作用在它身上。 -->
<button class="bb-banner hide" id="bbBanner" type="button">
<span class="bb-gift" id="bbGift" aria-hidden="true"></span>
<span class="bb-badge hide" id="bbBadge" aria-hidden="true"></span>
<span class="bb-txt"><b class="bb-t" id="bbTitle"></b><span class="bb-s" id="bbSub"></span></span>
<span class="bb-cta" id="bbCta"></span>
<i class="bb-shine" aria-hidden="true"></i>
</button>
</div>
</section>
<section class="screen" id="screen-archive" data-screen="archive">
<div class="page-hero">
<div class="hero-top">
<div class="hero-actions">
<button class="icon-btn glass" data-search="1" aria-label="搜索"><svg viewBox="0 0 24 24" width="19" height="19"><circle cx="11" cy="11" r="6.5" fill="none" stroke="currentColor" stroke-width="1.9"/><path d="M16 16l4.5 4.5" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/></svg></button>
<button class="icon-btn glass" data-toast="更多操作" aria-label="更多"><svg viewBox="0 0 24 24" width="19" height="19"><circle cx="5" cy="12" r="1.7"/><circle cx="12" cy="12" r="1.7"/><circle cx="19" cy="12" r="1.7"/></svg></button>
</div>
</div>
<!-- 这一块整体重排过一次,改动的理由都写在下面各处 -->
<div class="hero-info ar-hero">
<!-- 页面名。上一版把它降成了 13px、opacity .78 的一行眉批 ——
当时的理由是「底部 tab 上就写着档案,别拿 27px 再说一遍」,
字号该让给孕周,这点现在还成立。但让过头了:
这行是整个页面唯一出现宝宝小名的地方,糊在橙底上基本读不到。
这一版不靠字号抢回来(25px 仍然归 .ar-week),改成一枚毛玻璃小牌:
半透明白底 + 一圈描边,从橙色里浮出来;小名单独拎出来加粗放大,
「的成长档案」压一档跟在后面 —— 重音落在名字上,而不是「档案」上。
⚠️ 小名现在写进 #arTitleName,不再是 #arTitle 本身。
syncPregnancy() 里那句赋值跟着改了,别再往 #arTitle 上写 textContent,
那会把牌子里的图标和「的成长档案」一起冲掉。 -->
<h1 class="ar-title" id="arTitle"><svg class="ar-t-ico" viewBox="0 0 24 24" width="13" height="13" aria-hidden="true"><path d="M4 4.5A1.5 1.5 0 015.5 3H10a3 3 0 013 3v13a2.5 2.5 0 00-2.5-2.5H4z" fill="currentColor" opacity=".5"/><path d="M20 4.5A1.5 1.5 0 0018.5 3H14a3 3 0 00-3 3v13a2.5 2.5 0 012.5-2.5H20z" fill="currentColor"/></svg><b class="ar-t-name" id="arTitleName">宝宝</b><span class="ar-t-rest">的成长档案</span></h1>
<!-- 这两行 + 左边那个大小图标 + 下面的孕程条,都由 syncPregnancy() 按孕期信息重算。
emoji 用 aria-hidden 挡掉:读屏念成「玉米」会和紧跟着的「像一根玉米了」重复 -->
<div class="ar-lines">
<span class="ar-size" id="arSizeArt" aria-hidden="true">🌽</span>
<div>
<p class="ar-week" id="arLine1">孕 24 周 +3 天</p>
<p class="hero-p sub" id="arLine2">像一根玉米了 · 约 600 克</p>
</div>
</div>
<!-- 孕程原来是右边一个 84px 的圆环,和首页 hero 那个环长得一模一样,
两页来回切像同一个组件贴了两遍;它还横着吃掉近三分之一宽度,
逼得旁边的字只能写得又短又挤,「已陪伴 171 天」当时就只能拿个「·」
硬接在孕周后面,两个不相干的单位读起来像一句话。
换成一条通栏细进度条:高 5px,信息一点没少,左右还各空出一个角,
「孕程」和「已陪伴」这才各站各的位置。 -->
<div class="ar-prog"><i id="arBar"></i></div>
<div class="ar-foot">
<span>孕程 <b id="arPct">60%</b></span>
<span>已陪伴 <b id="arDays">171</b> 天</span>
</div>
</div>
</div>
<div class="page-body">
<!-- ========== 正在自动归档 + 生成档案(合成一张卡) ==========
顺序:标题 → 两个生成入口 → 归档条数统计。
周档案:每周一张长图,保存到相册或分享朋友圈;
完整档案:从记录第一天到今天,排成一本 A4 分页的册子,打印出来能直接装订。
两者都是 canvas 现画出来的,不依赖任何外部库。 -->
<div class="auto-card">
<div class="ac-head">
<span class="ac-h-l"><span class="live-dot"></span> 正在自动归档</span>
<span class="gen-badge" id="genWeekTag">第 24 周</span>
</div>
<!-- 说明文字拆进了下面两个入口各自的副标题:
谁能干什么写在谁身上,比笼统一句更好读。
「可打印」原本在这里的角标和按钮里各出现一次,现在只留按钮那处 -->
<div class="gen-btns">
<button class="gen-btn ripple" data-poster="week">
<svg viewBox="0 0 24 24" width="21" height="21" aria-hidden="true">
<rect x="3.5" y="4.5" width="17" height="16" rx="3" fill="none" stroke="currentColor" stroke-width="1.8"/>
<path d="M3.5 9.5h17M8 2.8v3.4M16 2.8v3.4" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
<rect x="7" y="12.5" width="5" height="4" rx="1.2" fill="currentColor"/>
</svg>
<span><b>本周档案</b><em>存相册 · 发朋友圈</em></span>
</button>
<button class="gen-btn ripple" data-poster="all">
<svg viewBox="0 0 24 24" width="21" height="21" aria-hidden="true">
<path d="M7 3.5h7.5L19 8v12.5H7z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
<path d="M14 3.5V8h5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
<path d="M10 12h6M10 15.5h6M10 19h3.5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</svg>
<span><b>完整档案</b><em>A4 分页 · 可装订</em></span>
</button>
</div>
<div class="ac-chips">
<span class="chip-stat">今日已归档 <b>4</b> 条</span>
<span class="chip-stat">累计 <b>386</b> 条</span>
<span class="chip-stat">来自 <b>9</b> 个孕期工具</span>
</div>
</div>
<div class="segmented" id="archiveSeg">
<span class="seg-thumb" id="segThumb"></span>
<!-- ⚠️ 这三个按钮的顺序必须和下面 .seg-panel 的 DOM 顺序一一对应。
initSegmented() 是按 .is-active 在兄弟节点里的**序号**算 #segThumb 的 x 偏移,
只改按钮不改面板 → 高亮停在「成长数据」、底下却显示时光轴。 -->
<button class="seg-btn is-active" data-seg="data">成长数据</button>
<button class="seg-btn" data-seg="timeline">时光轴</button>
<!-- 收藏数原本在下面那行 sec-title 里,那行整条删了,
数字搬到标签上(和分段控件里其它计数同一种写法),#favCount 仍由 JS 更新。
⚠️ 这里原来写死 18,而屏幕上点亮的星标只有 6 个 —— 数字与内容对不上。
现在默认 0,由 app.js 启动时按 DOM 里 `.star.is-on` 的**实际个数**回填
(档案页不是首屏,用户看不到中间态;点星标时再 ±1)。 -->
<button class="seg-btn" data-seg="moment">珍贵时刻 <i id="favCount">0</i></button>
</div>
<!-- 成长数据 -->
<div class="seg-panel is-active" data-panel="data">
<!-- ⚠️ 空态(未建档 / pregEmpty):syncPregnancy 会给本面板加 .is-empty,
把下面三块**全无数据源**的演示内容整块隐藏,改显示这行引导。
三块里没有一个数字来自接口:58.2kg / 31 次 / 600g / 双顶径 6.1cm 都是设计稿数字,
未建档用户看到就是纯假数据(2026-09-22 实测)。 -->
<div class="data-empty">还没有可统计的记录<br>去「数胎动」或「孕期体重」记一笔,这里会生成趋势</div>
<div class="stat-row data-demo">
<div class="stat-card">
<div class="sc-label">体重</div>
<div class="sc-value">58.2<small>kg</small></div>
<div class="sc-trend up">↑ 7.7kg</div>
<svg class="spark" viewBox="0 0 100 34"><polyline points="2,30 26,24 50,17 74,10 98,4" fill="none" stroke="#F4593C" stroke-width="2.6" stroke-linecap="round"/></svg>
</div>
<div class="stat-card">
<div class="sc-label">日均胎动</div>
<div class="sc-value">31<small>次</small></div>
<div class="sc-trend ok">正常</div>
<svg class="spark" viewBox="0 0 100 34"><polyline points="2,20 20,8 38,24 56,10 74,26 98,14" fill="none" stroke="#7FA86A" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg>
</div>
<div class="stat-card">
<div class="sc-label">估计体重</div>
<div class="sc-value">600<small>g</small></div>
<div class="sc-trend purple">P55</div>
<svg class="spark" viewBox="0 0 100 34"><polyline points="2,29 26,23 50,17 74,11 98,4" fill="none" stroke="#8B6FE0" stroke-width="2.6" stroke-linecap="round"/></svg>
</div>
</div>
<div class="chart-card data-demo">
<div class="cc-title">妈妈体重 · 孕12–24周</div>
<svg class="chart" viewBox="0 0 320 150">
<line class="grid" x1="4" y1="20" x2="316" y2="20"/>
<line class="grid" x1="4" y1="66" x2="316" y2="66"/>
<line class="grid" x1="4" y1="112" x2="316" y2="112"/>
<path class="band" d="M8,138 L83,120 L158,100 L234,76 L310,54 L310,14 L234,36 L158,60 L83,80 L8,98 Z"/>
<path class="area" d="M8,124 L83,106 L158,86 L234,62 L310,40 L310,146 L8,146 Z"/>
<polyline class="line" points="8,124 83,106 158,86 234,62 310,40"/>
<circle class="pt" cx="83" cy="106" r="6"/><circle class="pt" cx="158" cy="86" r="6"/><circle class="pt" cx="234" cy="62" r="6"/>
<circle class="pt fill" cx="310" cy="40" r="7"/>
</svg>
<div class="cc-x"><span>12周</span><span>16周</span><span>20周</span><span>24周</span></div>
<div class="cc-legend"><span class="lg lg-line">我的体重</span><span class="lg lg-band">推荐区间</span></div>
</div>
<div class="meas-card data-demo">
<div class="meas"><span class="m-name">双顶径</span><div class="m-bar" style="--p:54%"><i></i></div><div class="m-val">6.1<small>cm</small><em>P58</em></div></div>
<div class="meas"><span class="m-name">股骨长</span><div class="m-bar" style="--p:50%"><i></i></div><div class="m-val">4.4<small>cm</small><em>P52</em></div></div>
<div class="meas"><span class="m-name">腹围</span><div class="m-bar" style="--p:60%"><i></i></div><div class="m-val">19.6<small>cm</small><em>P61</em></div></div>
</div>
</div>
<!-- 时光轴 -->
<!-- 这里原来还压着一排分类 chips(全部/胎动/产检/来信/手记)。删了:
上面刚有一排「时光轴 / 成长数据 / 珍贵时刻」的分段,紧接着再来一排样子
几乎一样的标签,两排叠在一起谁管谁根本看不出来。
而且时光轴统共五条,一条一个分类,筛完最多剩两条 —— 为了从五条里挑两条
先读五个标签,不划算。每条底下本来就写着「来自「数胎动」」,
要认哪一类,读那行就够了。 -->
<div class="seg-panel" data-panel="timeline">
<div class="tl-card">
<div class="tl-item" data-cat="胎动">
<div class="tl-date" data-ga="now"></div>
<p class="tl-text">早上 9 点 10 分,他动了 <b>12 次</b>。你说那感觉像有人在里面轻轻敲门。</p>
<span class="tl-from">来自「数胎动」</span>
</div>
<div class="tl-item" data-cat="产检">
<div class="tl-date" data-ga="20"></div>
<p class="tl-text">大排畸那天,你第一次看清了他的侧脸。医生说「一切都好」,你在诊室外站了很久。</p>
<span class="tl-from">来自「孕检日历」+「B超影像」</span>
</div>
<div class="tl-item" data-cat="胎动">
<div class="tl-date" data-ga="18"></div>
<p class="tl-text">傍晚散步时,肚子里忽然有一下轻轻的翻涌 —— <b>第一次胎动</b>。你愣在原地,然后笑了。</p>
<span class="tl-from">来自「胎动记录」</span>
</div>
<div class="tl-item" data-cat="产检">
<div class="tl-date" data-ga="8"></div>
<p class="tl-text">第一次听见心跳,很快,像小火车。你把那 12 秒录了下来。</p>
<span class="tl-from">来自「胎心记录」</span>
</div>
<div class="tl-item" data-cat="手记">
<div class="tl-date" data-ga="1"></div>
<p class="tl-text">那两条线出现的早晨,你把它拍下来,发给了他。你们谁都没说话,笑了很久。</p>
<span class="tl-from">来自「妈妈手记」</span>
</div>
</div>
</div>
<!-- 珍贵时刻 -->
<!-- 同样删掉了一排分类 chips(全部/第一次/影像/声音/手写)。
六个格子的九宫格,本身一眼就扫完了,再给它配五个筛选标签是本末倒置。 -->
<div class="seg-panel" data-panel="moment">
<!-- 珍贵时刻的封面图标:原来是 emoji(🖼️💌👣🎧🤰✍️),
各系统渲染成的样子完全不同(拟物、彩色、还带阴影),
这里统一换成同一套线性扁平 SVG,颜色跟着各自的渐变底走 -->
<div class="moment-grid">
<button class="moment ripple" data-cat="影像 第一次" data-toast="第一张B超 · 孕8周">
<div class="mo-art a1">
<svg viewBox="0 0 48 48" aria-hidden="true">
<rect x="7" y="9" width="34" height="26" rx="5" class="f"/>
<rect x="7" y="9" width="34" height="26" rx="5" class="s"/>
<path d="M16 29a12 12 0 0 1 12-12" class="s"/>
<path d="M16 29a7 7 0 0 1 7-7" class="s"/>
<circle cx="16" cy="29" r="2.4" class="d"/>
<path d="M24 35v4M20 39h8" class="s"/>
</svg>
<i class="star is-on">★</i>
</div>
<div class="mo-title">第一张B超</div><div class="mo-meta" data-ga="8"></div><span class="mo-from">来自「B超影像」</span>
</button>
<!-- ⚠️ `.mo-from` 引的是**底 tab 的名字**,不是随便一句出处。
tab 叫什么,这里就必须写什么 —— 指过去的地方得真的存在。
上一批 tab 改叫「宝宝消息」时这三处跟着改过(标题「第一条消息」、
出处「来自「宝宝消息」」、data-toast),本批 tab 退回「宝宝来信」,
三处一起退。
⚠️ 宽度:来回都是等字数替换(5 字换 5 字、8 字换 8 字),
.mo-title / .mo-from 的排版账不用重算。
⚠️ data-toast 那句是点上去弹的提示,和标题同源,一起改,别只改一处 ——
它藏在属性里,只扫 textContent 的检查看不见它。 -->
<button class="moment ripple" data-cat="第一次" data-toast="第一封来信 · 孕12周">
<div class="mo-art a2">
<svg viewBox="0 0 48 48" aria-hidden="true">
<rect x="6" y="11" width="36" height="26" rx="5" class="f"/>
<rect x="6" y="11" width="36" height="26" rx="5" class="s"/>
<path d="M8.5 14.5l14 11a2.4 2.4 0 0 0 3 0l14-11" class="s"/>
</svg>
<i class="star is-on">★</i>
</div>
<div class="mo-title">第一封来信</div><div class="mo-meta" data-ga="12"></div><span class="mo-from">来自「宝宝来信」</span>
</button>
<button class="moment ripple" data-cat="第一次" data-toast="第一次胎动 · 孕18周">
<div class="mo-art a3">
<svg viewBox="0 0 48 48" aria-hidden="true">
<path d="M27 17c0 6-2.8 8.8-2.8 12.8 0 4-2.4 6.7-5.9 6.7s-6.3-2.7-6.3-6.7c0-5 2.9-7.9 2.9-12.8 0-4.1 2.6-7 5.9-7s6.2 2.9 6.2 7z" class="f"/>
<path d="M27 17c0 6-2.8 8.8-2.8 12.8 0 4-2.4 6.7-5.9 6.7s-6.3-2.7-6.3-6.7c0-5 2.9-7.9 2.9-12.8 0-4.1 2.6-7 5.9-7s6.2 2.9 6.2 7z" class="s"/>
<circle cx="31.5" cy="14.5" r="3" class="d"/>
<circle cx="35.6" cy="20.4" r="2.6" class="d"/>
<circle cx="36.8" cy="27" r="2.2" class="d"/>
</svg>
<i class="star is-on">★</i>
</div>
<div class="mo-title">第一次胎动</div><div class="mo-meta" data-ga="18"></div><span class="mo-from">来自「胎动记录」</span>
</button>
<button class="moment ripple" data-cat="声音" data-toast="正在播放 胎心录音 ♪">
<div class="mo-art a4">
<svg viewBox="0 0 48 48" aria-hidden="true">
<path d="M24 39S8 30.2 8 19.8C8 14.6 12 11.2 16.2 11.2c3.2 0 6 1.8 7.8 4.4 1.8-2.6 4.6-4.4 7.8-4.4 4.2 0 8.2 3.4 8.2 8.6C40 30.2 24 39 24 39z" class="f"/>
<path d="M24 39S8 30.2 8 19.8C8 14.6 12 11.2 16.2 11.2c3.2 0 6 1.8 7.8 4.4 1.8-2.6 4.6-4.4 7.8-4.4 4.2 0 8.2 3.4 8.2 8.6C40 30.2 24 39 24 39z" class="s"/>
<path d="M12.5 22.5h5.2l3-5.4 3.9 10.4 2.8-5h6.1" class="s"/>
</svg>
<i class="star is-on">★</i>
</div>
<div class="mo-title">胎心录音 12秒</div><div class="mo-meta" data-ga="20"></div><span class="mo-from">来自「胎心记录」</span>
</button>
<button class="moment ripple" data-cat="影像 第一次" data-toast="第一张孕肚照 · 孕22周">
<div class="mo-art a5">
<svg viewBox="0 0 48 48" aria-hidden="true">
<path d="M6 17a4 4 0 0 1 4-4h5l2.6-4.2h12.8L33 13h5a4 4 0 0 1 4 4v17a4 4 0 0 1-4 4H10a4 4 0 0 1-4-4z" class="f"/>
<path d="M6 17a4 4 0 0 1 4-4h5l2.6-4.2h12.8L33 13h5a4 4 0 0 1 4 4v17a4 4 0 0 1-4 4H10a4 4 0 0 1-4-4z" class="s"/>
<circle cx="24" cy="25.5" r="7" class="s"/>
<circle cx="24" cy="25.5" r="2.6" class="d"/>
</svg>
<i class="star is-on">★</i>
</div>
<div class="mo-title">第一张孕肚照</div><div class="mo-meta" data-ga="22"></div><span class="mo-from">来自「孕肚照」</span>
</button>
<button class="moment ripple" data-cat="手写" data-toast="给宝宝的回信 · 孕23周">
<div class="mo-art a6">
<svg viewBox="0 0 48 48" aria-hidden="true">
<path d="M31.4 8.6a4 4 0 0 1 5.7 5.7L17.6 33.8l-8.2 2.5 2.5-8.2z" class="f"/>
<path d="M31.4 8.6a4 4 0 0 1 5.7 5.7L17.6 33.8l-8.2 2.5 2.5-8.2z" class="s"/>
<path d="M28.5 11.5l5.7 5.7" class="s"/>
<path d="M14 41h22" class="s"/>
</svg>
<i class="star is-on">★</i>
</div>
<div class="mo-title">给宝宝的回信</div><div class="mo-meta" data-ga="23"></div><span class="mo-from">来自「我的回信」</span>
</button>
</div>
</div>
<button class="album-card ripple" data-toast="纪念册第 2 册编纂中……">
<div class="al-book"><span class="al-leaf">🌱</span><span class="al-tag">孕早期<br>1–13周</span></div>
<div class="al-body">
<div class="al-title">纪念册 <em>交给未来的孩子</em></div>
<p class="al-desc">第 1 册已成册(32 页)· 第 2 册编纂中<br>等他长大,这些会替你把话说完。</p>
<div class="al-bar"><i style="--p:62%"></i></div>
<div class="al-meta">孕中期 62% · 预计孕28周可成册</div>
</div>
</button>
</div>
</section>
<section class="screen" id="screen-me" data-screen="me">
<div class="me-top me-v2">
<!-- 2026-10-01「我的」页改版:头部从实心橘渐变 + 白卡,改成蜜桃 → 珊瑚的柔光渐变,
用户信息直接压在渐变上(白字),下面一张金色「兜豆兑换商城」卡半压在头部边缘。
装饰:两团柔光 + 几颗小星星,纯 CSS/SVG,不加图片请求。 -->
<i class="mt-glow g1" aria-hidden="true"></i><i class="mt-glow g2" aria-hidden="true"></i>
<svg class="mt-stars" viewBox="0 0 120 60" aria-hidden="true"><path d="M20 14l1.6 4.2 4.4 1.6-4.4 1.6L20 25.6l-1.6-4.2-4.4-1.6 4.4-1.6z" fill="#fff" opacity=".75"/><path d="M92 8l1 2.6 2.6 1-2.6 1L92 15.2l-1-2.6-2.6-1 2.6-1z" fill="#fff" opacity=".6"/><circle cx="64" cy="22" r="1.6" fill="#fff" opacity=".55"/><circle cx="108" cy="40" r="1.2" fill="#fff" opacity=".5"/></svg>
<div class="hero-top">
<!-- (商城图标已移除:进商城统一走下方「兜爸心选」模块 meMallCard)
<button class="icon-btn plain" id="btnMall" aria-label="进商城">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true">
<path d="M5 8h14l-1.2 11a2 2 0 0 1-2 1.8H8.2a2 2 0 0 1-2-1.8z" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/>
<path d="M8.6 10V6.8a3.4 3.4 0 0 1 6.8 0V10" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/>
</svg>
</button>
-->
<!-- 扁平化齿轮:描边式矢量,替换原本的 ⚙️ emoji -->
<button class="icon-btn plain" data-modal="modalProfile" aria-label="设置">
<svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true">
<path d="M12 15.2a3.2 3.2 0 1 0 0-6.4 3.2 3.2 0 0 0 0 6.4z" fill="none" stroke="currentColor" stroke-width="1.7"/>
<path d="M19.4 13.5a1.5 1.5 0 0 0 .3 1.65l.06.05a1.8 1.8 0 1 1-2.55 2.55l-.05-.06a1.5 1.5 0 0 0-1.65-.3 1.5 1.5 0 0 0-.91 1.37v.14a1.8 1.8 0 1 1-3.6 0v-.07a1.5 1.5 0 0 0-.98-1.37 1.5 1.5 0 0 0-1.65.3l-.05.06a1.8 1.8 0 1 1-2.55-2.55l.06-.05a1.5 1.5 0 0 0 .3-1.65 1.5 1.5 0 0 0-1.37-.91h-.14a1.8 1.8 0 1 1 0-3.6h.07a1.5 1.5 0 0 0 1.37-.98 1.5 1.5 0 0 0-.3-1.65l-.06-.05A1.8 1.8 0 1 1 8.5 4.01l.05.06a1.5 1.5 0 0 0 1.65.3h.07a1.5 1.5 0 0 0 .91-1.37v-.14a1.8 1.8 0 1 1 3.6 0v.07a1.5 1.5 0 0 0 .91 1.37 1.5 1.5 0 0 0 1.65-.3l.05-.06a1.8 1.8 0 1 1 2.55 2.55l-.06.05a1.5 1.5 0 0 0-.3 1.65v.07a1.5 1.5 0 0 0 1.37.91h.14a1.8 1.8 0 1 1 0 3.6h-.07a1.5 1.5 0 0 0-1.37.91z" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
</div>
<!-- 整张卡可点:点它和点右上角齿轮都会打开「我的资料」弹窗。
这里保持 div 而不是 button——里面还嵌着头像的编辑角标,
button 套 button 是非法结构,浏览器会把 DOM 拆开 -->
<div class="me-card ripple" data-modal="modalProfile" role="button" tabindex="0">
<div class="me-avatar" id="meAvatar">
<svg viewBox="0 0 40 40" width="34" height="34" aria-hidden="true">
<!-- 卡片改成白底后,写死的 #fff 剪影会直接看不见,改由 .me-avatar 的 color 决定 -->
<circle cx="20" cy="14.5" r="7" fill="currentColor"/>
<path d="M6.5 35c0-7.2 6-11.5 13.5-11.5S33.5 27.8 33.5 35z" fill="currentColor"/>
</svg>
<img id="meAvatarImg" alt="">
<i class="av-edit" aria-hidden="true">
<svg viewBox="0 0 24 24" width="11" height="11">
<path d="M4 20h16" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/>
<path d="M15.6 4.4a2.3 2.3 0 0 1 3.3 3.3L9.7 16.9l-4.3 1 1-4.3z" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linejoin="round"/>
</svg>
</i>
</div>
<div class="me-info">
<div class="me-name" id="meName">未登录</div>
<!-- ⚠️ 这里先后住过两样东西,两样都已经删了,别再往回搬:
① 最早是「孕 24 周 +3 天 · 预产期 12月22日」,由 syncPregnancy() 刷。
删它是因为**重复**:往下滚一屏就是孕期卡,孕周、预产期、
剩余天数在那儿写得更全。
② 然后换成了一句话签名 <p class="me-bio" id="meBio">,
本批按用户要求(「我的资料里的一句话签名可以去掉」)连同
弹窗里的 #pfBio 输入框、js 里的 syncBio()、state.userBio 一起删。
⚠️ 必须一起删:只删输入框、留下这行展示,它就成了一句
谁也改不动的死文案 —— 用户会在资料弹窗里翻来翻去找哪儿能改。
⚠️ syncPregnancy() 里那行 $('.me-info p') 早就删掉了(它取的是
**第一个** <p>)。现在第一个 <p> 是下面的「已连续记录」,
谁要是把那行加回来,会把连续记录天数冲掉,而且是静默的。 -->
<span class="me-tag hide" id="meTag"></span>
<p id="meRecordLine">已连续记录 <b>42</b> 天</p>
</div>
<i class="me-chev" aria-hidden="true">›</i>
</div>
</div>
<div class="page-body no-top">
<!-- 毛玻璃底下的彩色光斑(2026-10-04 用户:「卡片毛玻璃一点,现在白底平铺不好看」)。
卡片是半透明 + backdrop-filter,必须垫点颜色才透得出玻璃感;纯 CSS,不加图片请求。 -->
<i class="me-orbs" aria-hidden="true"><b class="o1"></b><b class="o2"></b><b class="o3"></b><b class="o4"></b></i>
<!-- 退出登录已移到个人资料卡右上角的图标按钮(#meLogoutBtn) -->
<!-- ⚠️ 这两处的 data-modal 从 modalPreg 改成了 modalProfile:
「编辑孕期信息」那张弹窗已整个并进「我的资料」(见文件末尾那段说明)。
小名 / 性别 / 预产期现在只有一处能改,改完照样 syncPregnancy() 回刷这张卡。 -->
<!-- ⚠️ 「(必填)」是独立的 <span class="sec-req">,**不能**塞进 .sec-more 那颗按钮里 ——
.sec-more 是「编辑 ›」的可点区域,把「必填」并进去等于扩大了热区,
用户想读一眼提示、结果弹出了编辑窗。它也不能只是写进标题文本里:
要单独一个元素才能上品牌色。
⚠️ 用品牌色不用灰:「必填」是一句**要求**,写成灰小字就退成了附注,没人会去填。
宽度账(.page-body 左右各 18 → 可用 284,.sec-title 是 flex、gap 8):
竖条 4 + 8 + 「孕期信息」4×17.5=70 + 8 + 「(必填)」4×12=48 + 8 + 「编辑 ›」≈34
= 180 ≤ 284 ✓,余 104px,宽裕。 -->
<!-- 2026-10-01:按用户要求孕期卡和兜豆卡对调 —— 孕期卡现在半压在头部底边上,所以去掉了上方的
「孕期信息(必填)· 编辑 ›」标题行(标题压在渐变上很乱);整卡本身就点开资料弹窗,
「必填」挪进弹窗里那张「孕期信息」卡的角标。 -->
<button class="preg-card ripple" data-modal="modalProfile">
<div class="pc-row">
<!-- 孕周进度环 + 蜷着的宝宝。
上一版是一个细线条的孕妇侧影:笔画太细、那条「腿」像根多余的杂线,
肚子里那颗浅色小点在 52px 上几乎看不见,凑近才认得出画的是什么。
换掉的理由不只是好看 —— 这张卡讲的就是「走到哪儿了」
(孕 24 周 +3 天 / 还有 110 天 / 一条进度条 / 早中晚期三段),
所以图标本身就画成一圈进度:外圈是整个孕期,实心那段是已经走过的,
中间蜷着的是宝宝。图标和卡片说的是同一件事。
⚠️ 这一圈是**活的**:syncPregnancy() 按真实孕周重写 #pcRing 的
stroke-dasharray,和下面那条 .pc-bar 同源(同一个 pct)。
这里写死的 "45 73" 只是 JS 跑起来之前的占位(≈62%,对应 r=11.6
的周长 72.9),不是设计值 —— 要改进度去改 syncPregnancy,别调这里。
⚠️ 下面这几处颜色跟着 .preg-card 的配色走
(橘 → 雾蓝 → 紫粉 → 现在的深紫渐变 + 白)。
它们是 SVG presentation attribute,不在 style.css 里,
改配色时最容易漏掉的就是这一块 —— 上一版深紫 #7B5BD6 在这儿出现
**三次**(进度环 stroke、宝宝头 fill、身子 stroke),
只改第一处就会得到「紫环配蓝宝宝」。
⚠️ 卡片现在是深底,整个图标就是**一色白**,上面那个「三处」的坑
因此暂时看不出来了 —— 但它随时会回来:下次谁把配色改回浅底,
就得重新给这三处各挑一个深色。smoke 里钉着「图标里所有颜色一致」,
漏改一处就会红。
⚠️ 底环用 stroke-opacity 调暗,**不能**和进度环一样是实白 ——
两圈一样白就等于没有进度环,那一圈永远看着是满的。
用 stroke-opacity 而不是 rgba():presentation attribute 里
rgba 各家浏览器支持不一,stroke-opacity 是 SVG 1.1 的正经属性。 -->
<div class="pc-ico">
<svg viewBox="0 0 32 32" width="27" height="27" aria-hidden="true">
<circle cx="16" cy="16" r="11.6" fill="none" stroke="#F0704F" stroke-opacity=".3" stroke-width="3.2"/>
<circle id="pcRing" cx="16" cy="16" r="11.6" fill="none" stroke="#F0704F" stroke-width="3.2"
stroke-linecap="round" stroke-dasharray="45 73" transform="rotate(-90 16 16)"/>
<circle cx="17" cy="12.9" r="3.2" fill="#F0704F"/>
<path d="M13.8 17.2c.3 3.6 3 5.1 6 4.1" fill="none" stroke="#F0704F" stroke-width="3.4" stroke-linecap="round"/>
</svg>
</div>
<div class="pc-main"><div class="pc-title" id="pcTitle">孕 24 周 +3 天</div><div class="pc-sub" id="pcSub">预产期 2024年12月22日 · 还有 110 天</div></div>
<i class="chev">›</i>
</div>
<div class="pc-bar"><i id="pcBar" style="--p:60%"></i></div>
<div class="pc-stage"><span>孕早期</span><span class="cur" id="pcStage">孕中期 · 当前</span><span>孕晚期</span></div>
</button>
<!-- 兜豆兑换商城主卡(2026-10-01):半压在头部渐变边缘,是「我的」页最跳的一张。
点击走 openExchange()(app.js)—— 链接优先取应用后台,取不到再用写死的兜底地址。
#meExchSub 未登录写「攒兜豆,好物免费兑」,拿到余额后改成「兜豆 N · 可兑 M 件」(loadDou())。 -->
<!-- 授权完善资料:H5 → navigateTo → 小程序授权页(头像/昵称/手机号) → box_app /auth/profile 落库 -->
<div class="list-card" style="margin-top:14px" id="meAuthBox">
<button class="li ripple" id="meAuthCard">
<span class="li-ico o"><svg viewBox="0 0 24 24" width="19" height="19" aria-hidden="true"><rect x="5" y="10.5" width="14" height="9.5" rx="2.5" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M8.5 10.5V8a3.5 3.5 0 0 1 7 0v2.5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><path d="M12 15v2" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></span>
<span class="li-t">授权完善资料</span>
<span class="li-v">获取手机号 / 昵称 / 头像</span>
<i class="chev">›</i>
</button>
</div>
<!-- 兜豆兑换:2026-10-04 用户要求「收起来,不要这么大地展示」—— 原来的大卡(标题+热门 3 件好物)收成一条横条,
和下面「兜爸心选商城」同款。#meExchCard / #meExchSub 不变(openExchange、loadDou 照旧);
#meExchDrop 已删,renderDrop() 里有 if (box) 判空,不用改 JS。 -->
<button class="mall-strip exch ripple" id="meExchCard">
<span class="ms-ico" aria-hidden="true"><img src="assets/img/dou-coin.svg?v=20261002" width="28" height="28" alt=""></span>
<span class="ms-main"><b>兜豆兑好物</b><span id="meExchSub">攒兜豆,好物免费兑</span></span>
<span class="ms-go">去兑换<i>›</i></span>
</button>
<!-- ================= 孕育顾问(企业微信入口) =================
常驻在孕期卡下面:上面那张卡讲「你走到哪儿了」,这张接着说
「路上有人陪你」,是同一段话的下一句;而「设置与服务」那组是
工具箱,把一个真人服务塞进去会被当成一行设置项划过去。
⚠️ 故意**不挂 data-modal="modalWx"**。那条通用通道直接调 openModal,
会绕过 openWx() —— 弹窗的模式类名、引导语、上次残留的红字和
输入内容、还有 wxPending 全都不会被重置。这里必须走 JS 那条路。
⚠️ 这张卡历史上**不随解锁状态隐藏**(资源库顶部那条 banner 会),
定位是「我的」页的常驻入口。本批(第九批)按用户要求把整块
「专属福利」先隐藏 —— 见下面 #meWelfareHidden 那段注释;
上面这条「常驻」的旧约定随之暂停,恢复时把该容器的 display:none 去掉即可。 -->
<!-- ⚠️ 本批(第九批)按用户要求**先隐藏**「专属福利」整块(标题 + 孕育顾问卡)。
用外层容器 display:none 而不是删 DOM,原因有二:
① #meWxCard 的点击绑定(app.js openWx('gift'))、syncBabyWords() 对
#meWxTitle/#meWxSub 的写入都还引用它们,删了会当场报错;
② 保留 DOM 便于随时一键恢复(去掉这里的 display:none 即可)。
⚠️ 隐藏的只是这一块。下面「甄选好物 / 进入商城」(#meMallCard)**不动** ——
它是另一张卡,用户没要求藏。
⚠️ 资源库顶部那条 #rlGift banner 不在这一块里,本次未动。 -->
<div id="meWelfareHidden" style="display:none">
<h2 class="sec-title dark">专属福利</h2>
<button class="wx-card ripple" id="meWxCard">
<!-- ⚠️ 这一版是**三段竖排**:角标行 / 图文行 / 通栏 CTA。
上一版是四段(角标 / 标题 / 图文 / CTA),本批按用户要求
「商品名放到到手价上边、跟到手价对齐」,把整个标题行搬进了右列
.wx-main —— 商品名和价格从此共用同一条左缘,这就是「对齐」的实现。
同时「每人领取一份放到商品名右边、左边整个位置留给图片」。
卡内净宽 = 320 − 页边 36 − 卡内边 36 = **248px**(下面所有账都对着它)
① 图文行:.wx-fig 76 + gap 12 → 右列 .wx-main = 248 − 76 − 12 = **160px**
② 标题行(住在 160px 的右列里;本批起两件**竖排**,各占一整行):
「可心柔婴儿纸巾」7 字 × 16 = 112 ≤ 160 ✓ 余 48px
「限领一份」 4 字 × 11.5 = 46 ≤ 160 ✓ 余 114px
⚠️ 上一版是横排:105 + gap 5 + 42 = 152 ≤ 160,**只余 8px**,
而且那 8px 是按 15px 字号死算的。微信有一个全局字体大小
滑块,用户一调大,105px 的商品名当场涨到 130+,余量瞬间
蒸发、ellipsis 真的触发 —— 商品名被啃成「可心柔婴儿纸…」。
**这就是用户本批报的那个 bug**(原话「把限领一份儿放到
可心柔婴儿纸巾的后边」,追问后确认他看到的是被切断的商品名)。
⚠️ 竖排把「两件抢同一条 160px」这个根子问题去掉了,不是把
临界点往后推。所以字号也一并回到 16 / 11.5,不再需要压字号。
**别为了省一行高度改回横排。**
③ 右列其余两行(都在 160px 里):
「到手价 ¥0」= 3×11.5 + gap 5 + ¥0 ≈ 26 → 65.5 ✓
副文「加小汤圆的孕育顾问领取」11 × 12.5 = 137.5 ✓ 一行;
小名满 12 字时 20 字 = 250 → 折两行,line-clamp:2 兜住
④ 高度账 —— 这就是「左边整个的位置都留给图片」:
.wx-main 高 = 19.5(head) + 5 + 26(price) + 5 + 18.1(sub 一行) ≈ 73.6
sub 折两行时 ≈ 91.8
所以 .wx-fig **不写死高度**,用 align-self:stretch + min-height:92 ——
图片永远填满左列整个高度:短文案时由 92 撑住卡高,
长小名把右列顶到 91.8 时也不会被超过。
⑤ CTA 通栏 248px。 -->
<!-- 角标行:只有一枚角标,右对齐 = 卡的右上角。
⚠️ 这一行**故意是空的**,只装角标。看着浪费了 18px 高,但角标并回
别处必然挤爆某一行,而挤爆的表现是商品名被省略号啃掉。
⚠️ 角标**仍然是正常流里的一枚标签**,绝不能改回 position:absolute
骑在卡边上:.wx-card 挂着 .ripple,overflow:hidden 会把它裁掉半截。
那正是用户早先说的「限量福利被切了」,已经犯过一次,别再犯。 -->
<span class="wx-top">
<!-- ⚠️ 文案「限量福利」是刻意避开「免费领」的:下面 CTA 已经写了
「立即免费领取」,角标再写一遍「免费领」是同一张卡里重复吆喝。 -->
<span class="wx-badge">限量福利</span>
</span>
<span class="wx-body">
<!-- 左边这块是**商品占位插画**(一包抽纸),不是最终图。
⚠️ 类名是 .wx-fig,**绝不能**叫 .wx-qr —— 那个类是弹窗里的
企业微信二维码,测试里有一条「全页只有一处二维码」的断言,
蹭上去会当场红。
⚠️ 「示例图 · 待替换」这行字画在 svg **内部**,换成真实商品图时
它跟着一起消失,不会留下一行孤零零的说明。
⚠️ svg 的 width/height 从写死的 88 改成了 100%:现在框是
76 宽 × 拉伸高,写死 88 会顶出去。换真图时也照此办理。
⚠️ 里面是**内联 svg**,object-fit 对它**不生效** —— 生效的是
preserveAspectRatio(默认 xMidYMid meet = contain)。
64×64 的方图放进 76×92 的框会上下各留 8px 空条,
露出卡的橙色渐变;.wx-fig 的 background:#FFF1E6 就是来堵这个的
(取的是下面那张 <rect> 自己的颜色,所以看不出接缝)。
别改成 slice(cover)—— 那会把抽纸盒裁掉头尾。 -->
<span class="wx-fig" aria-hidden="true">
<svg viewBox="0 0 64 64" width="100%" height="100%">
<rect x="2" y="2" width="60" height="60" rx="14" fill="#FFF1E6"/>
<!-- 抽纸软包 -->
<path d="M17 22h30v26a3 3 0 0 1-3 3H20a3 3 0 0 1-3-3V22z" fill="#fff" stroke="#E8A98C" stroke-width="1.6" stroke-linejoin="round"/>
<path d="M17 22c0-3.6 3-6.4 7-6.4h16c4 0 7 2.8 7 6.4" fill="#FFE1D2" stroke="#E8A98C" stroke-width="1.6" stroke-linejoin="round"/>
<!-- 抽出来的那一张 -->
<path d="M27.5 22c0-2.6 2-4.6 4.5-4.6s4.5 2 4.5 4.6" fill="none" stroke="#D85F38" stroke-width="1.8" stroke-linecap="round"/>
<path d="M23 33h18M23 39h13" stroke="#F2C7B3" stroke-width="1.8" stroke-linecap="round"/>
<text x="32" y="59.5" text-anchor="middle" font-size="6.4" fill="#B79C90">示例图 · 待替换</text>
</svg>
</span>
<span class="wx-main">
<!-- 标题行:商品名独占一行,「限领一份」排在它**下面**。
⚠️⚠️ 本批(第八批)把这两件从横排改成竖排,方向和上一批**相反**。
上一批是「限领在商品名右侧」,可右列只有 160px,两件抢同一条宽度,
当时那笔账只余 8px —— 于是商品名被省略号啃成「可心柔婴儿纸…」。
用户这次报的就是这个现象(原话「把限领一份儿放到可心柔婴儿纸巾的
后边」,问过之后确认他看到的是被切断的商品名)。
竖排之后标题独占整行 160px,7 字 × 16 = 112,宽度争抢从根上消失。
⚠️ 别为了「省一行高度」改回 flex-direction:row —— 那等于把这个
bug 原样装回来,而且它在短商品名下不复现,测不出来。
⚠️ .wx-head 仍必须是 .wx-main 的**第一件**、排在 .wx-price 前面 ——
用户早先的原话是「商品名放到到手价上边」,两批要求叠加在一起。
⚠️ 别把「限领一份」合回 .wx-title 凑成一个字符串:合成一个字符串
就只剩一个字号,「限领」不再是商品名底下那行灰色小字,
用户要的那个视觉层级整个没了。 -->
<span class="wx-head">
<span class="wx-title" id="meWxTitle">可心柔婴儿纸巾</span>
<!-- ⚠️ 文案「限领一份」和资源库 banner 的 .rlg-lim **必须逐字相同**
(用户早先要求两张卡「一模一样」,smoke 里有一条断言钉着)。
改这句就得同时改 .rlg-lim,只改一边会当场红。
⚠️ 「(每人限领一份)」砍成「限领一份」是**第七批**被 8px 余量逼的
(那时它和商品名横排、抢同一条 160px)。第八批改竖排之后
它独占整行,8 字 × 11.5 = 92 ≤ 160,其实又放得下了。
仍然保持 4 字,理由换成「用户没要求改字」+ 两张卡必须逐字相同。
别拿「现在放得下」当作可以随手改回长文案的依据。 -->
<span class="wx-lim">限领一份</span>
</span>
<!-- 价格行 —— 这一版「电商化」的主角。
⚠️ 这里**只写到手价,不写划掉的原价**。原价是编出来的数字,
而这是一个真实品牌的真实商品:划线价属于价格宣称,
编一个就成了虚假标价。「¥0」不是新承诺,它就是这张卡
从第一版起一直写着的「立即免费领取」换个说法 ——
同一件事用电商的排版语言再说一遍,没有多承诺任何东西。
⚠️ 「免费领」这个商务承诺本身仍未获确认(第四批提出,见 CHANGELOG
的「已知不做」)。要撤这个承诺的话,撤的是整张卡,不是这一行。 -->
<span class="wx-price"><i>到手价</i><b>¥0</b></span>
<!-- 静态兜底写「宝宝的」,不写「小汤圆」:用户还没填小名时,
屏幕上不该出现一个他从没输入过的名字。填了之后
syncBabyWords() 会把这里换掉。
⚠️ 小名的挂点在这一行,不在上面的标题里,也不在下面的 CTA 里。
改 syncBabyWords() 时别找错行。 -->
<span class="wx-sub" id="meWxSub">加宝宝的孕育顾问领取</span>
</span>
</span>
<!-- CTA 现在是**通栏**的(上一版只占右列 159px)。整卡 248px 的实心按钮
是电商卡的标准收尾,也是这张卡唯一的动作。
⚠️ 必须是 <span> 不能是 <button>:外层 .wx-card 本身就是
<button>,按钮套按钮是非法 HTML。 -->
<span class="wx-cta">立即免费领取</span>
</button>
</div>
<!-- 心选商城:改成一条横幅(原来是带大按钮的电商卡,和上面的兑换卡抢主角)。id/点击逻辑不变(goMall)。 -->
<button class="mall-strip ripple" id="meMallCard">
<span class="ms-ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22"><path d="M5 8h14l-1.2 11a2 2 0 0 1-2 1.8H8.2a2 2 0 0 1-2-1.8z" fill="#fff" stroke="currentColor" stroke-width="1.7" stroke-linejoin="round"/><path d="M8.6 10V6.8a3.4 3.4 0 0 1 6.8 0V10" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg>
</span>
<span class="ms-main"><b>兜爸心选商城</b><span>为宝宝甄选的母婴好物</span></span>
<span class="ms-go">逛一逛<i>›</i></span>
</button>
<h2 class="sec-title dark">常用服务</h2>
<!-- 设置与服务:列表改成四宫格。按钮的 id / data-open / data-set 全部沿用,JS 不用动。
#meBoxCard 仍由盲盒数据控制 .hide;#meBoxV 显示成右上角小角标。 -->
<div class="svc-grid">
<button class="svc ripple hide" id="meBoxCard"><span class="sv-ico o"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><rect x="4.5" y="10.5" width="15" height="9.5" rx="2" fill="none" stroke="currentColor" stroke-width="1.8"/><rect x="3.3" y="7.2" width="17.4" height="3.6" rx="1.3" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M12 7.2V20M12 7.2C10.4 3.8 7 4.3 8 6.4c.6 1 2.4.9 4 .8zM12 7.2c1.6-3.4 5-2.9 4-.8-.6 1-2.4.9-4 .8z" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linejoin="round"/></svg></span><span class="sv-t">孕期卡册</span><span class="sv-badge" id="meBoxV"></span></button>
<button class="svc ripple" data-open="detailSetting" data-set="privacy"><span class="sv-ico p"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><rect x="4.5" y="10" width="15" height="10.5" rx="3" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M8.2 10V7.6a3.8 3.8 0 0 1 7.6 0V10" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/><circle cx="12" cy="15.2" r="1.5" fill="currentColor"/></svg></span><span class="sv-t">隐私权限</span></button>
<button class="svc ripple" data-open="detailSetting" data-set="help"><span class="sv-ico o"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M12 4.2c4.5 0 7.8 2.8 7.8 6.4s-3.3 6.4-7.8 6.4c-.7 0-1.3-.06-1.9-.18l-3.6 2.3a.4.4 0 0 1-.6-.45l.8-2.9c-1.5-1.16-2.3-2.7-2.3-4.4 0-3.6 3.1-6.4 7.6-6.4z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M9.9 9.4a2.2 2.2 0 0 1 4.2.8c0 1.5-2.1 1.6-2.1 3" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round"/></svg></span><span class="sv-t">帮助反馈</span></button>
<!-- 2026-10-04 意见反馈(用户:「我的页面里加一个客服反馈,功能上的反馈、功能上的建议」)→ #modalFeedback,提交到 box_app /api/mp/public/mama-box/feedback -->
<button class="svc ripple" id="meFeedback"><span class="sv-ico g"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M5.5 4.8h13a1.7 1.7 0 0 1 1.7 1.7v8.6a1.7 1.7 0 0 1-1.7 1.7H11l-4.1 3a.4.4 0 0 1-.64-.32V16.8H5.5a1.7 1.7 0 0 1-1.7-1.7V6.5a1.7 1.7 0 0 1 1.7-1.7z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/><path d="M8.2 9.3h7.6M8.2 12.5h4.8" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></span><span class="sv-t">意见反馈</span></button>
<button class="svc ripple" data-open="detailSetting" data-set="about"><span class="sv-ico b"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><circle cx="12" cy="12" r="8.2" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M12 11v5.4" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/><circle cx="12" cy="7.9" r="1.15" fill="currentColor"/></svg></span><span class="sv-t">关于我们</span></button>
</div>
<!-- 退出授权登录:仅已授权时显示(applyRemoteProfile 控制 style.display)。
调 box_app /auth/logout —— 只翻授权状态,不清昵称/头像/手机号等资料 -->
<button class="me-logout ripple" id="meLogoutCard" style="display:none">退出授权登录</button>
<p class="me-ver">兜爸心选 · v3.2.1</p>
</div>
</section>
</main>
<!-- ================= 孕期小知识 · 这一周 详情页(2026-10-01)=================
首页 #pregTips 下面四个入口点进来:宝宝 / 妈妈 / 饮食 / 留心 四张卡竖排、全文不截断;
点哪个入口就滚到哪张并闪一下。内容由 renderTips() 写入 ptBaby/ptMom/ptEat/ptCare。 -->
<section class="detail" id="detailTips">
<div class="detail-bar">
<button class="dt-back" data-detail="close">‹</button>
<span class="dt-title" id="ptDtTitle">孕期小知识</span>
</div>
<div class="detail-body pt-page">
<!-- 本周每日小知识:第 1 天到今天,今天在最上面;以后的几天锁着(与首页 7 个圆点同一口径) -->
<h3 class="pt-sub">本周每日小知识</h3>
<div class="pt-list" id="ptList"></div>
<h3 class="pt-sub">这一周</h3>
<div class="pt-grid" id="ptGrid">
<div class="pt-card pc-baby" data-pt="baby"><span class="pt-ico" id="ptBabyIco" aria-hidden="true">👶</span><b>宝宝这周</b><p id="ptBaby"></p></div>
<div class="pt-card pc-mom" data-pt="mom"><span class="pt-ico" aria-hidden="true">🤰</span><b>妈妈身体</b><p id="ptMom"></p></div>
<div class="pt-card pc-eat" data-pt="eat"><span class="pt-ico" aria-hidden="true">🥗</span><b>吃点什么</b><p id="ptEat"></p></div>
<div class="pt-card pc-care" data-pt="care"><span class="pt-ico" aria-hidden="true">📋</span><b>这周留心</b><p id="ptCare"></p></div>
</div>
<p class="pt-foot">仅供参考,身体有任何不适请以医生意见为准</p>
</div>
</section>
<!-- ================= AI 小结 内容页 ================= -->
<section class="detail" id="detailAI">
<div class="detail-bar">
<button class="dt-back" data-detail="close">‹</button>
<span class="dt-title">AI 孕育小结</span>
<button class="dt-plain" data-toast="小结每天 21:00 自动生成">⋯</button>
</div>
<!-- 这一页原来是「大卡 + 三条 li + 一串行」,从上到下全是罗列,没有层次。
重做成三段式:今日卡(深色,一眼看完今天)→ 三张建议卡(分类色 + 一句标题
一句正文)→ 往期时间轴(左边一条线串起来)。每段之间用带序号的小标题隔开。 -->
<div class="detail-body ai-body">
<!-- ① 今日卡:整页唯一的深色块,越往下越浅,视线自然从这里开始 -->
<section class="ai-today">
<span class="ai-glow" aria-hidden="true"></span>
<span class="ai-glow b" aria-hidden="true"></span>
<div class="ai-t-top">
<span class="ai-week" id="aiWeek">孕24周+3</span>
<span class="ai-date" id="aiDate">9月3日 周二</span>
</div>
<!-- 一句话概括今天,字号最大;右上角那枚小圆点是「今日状态」 -->
<span class="ai-mood" id="aiMood">安心</span>
<!-- renderAI() 会用 AI_LOGS[0].txt 覆盖这句,这里写的是渲染前的兜底。
口吻同样是宝宝第一人称,别让它在渲染前闪一句第三人称的旧话 -->
<p class="ai-hero-txt" id="aiTxt">妈妈,今天你数到我动了 3 次。我今天精神特别好,这是踏实的一天。</p>
<div class="ai-stats" id="aiStats"></div>
</section>
<div class="ai-sec">
<span class="ai-sec-n">01</span>
<!-- 原来叫「今天想对你说」。这三张卡的内容是「固定这两个时段陪他待一会儿」
「按平时胃口来就行」这类可执行的动作,是建议不是抒情,标题得对上 -->
<h3>今日小建议</h3>
<i class="ai-sec-line"></i>
</div>
<div class="ai-tips" id="aiTips"></div>
<div class="ai-sec">
<span class="ai-sec-n">02</span>
<h3>往期小结</h3>
<i class="ai-sec-line"></i>
<button class="sec-more" data-toast="已加载全部 30 天小结">近 30 天 ›</button>
</div>
<div class="ai-logs" id="aiLogs"></div>
<p class="unlock">小结由 AI 依据你的记录生成,是陪你一起看记录的伙伴,不代替医生的判断。</p>
</div>
</section>
<!-- ================= 资源库 列表页 ================= -->
<section class="detail" id="detailResList">
<div class="detail-bar">
<button class="dt-back" data-detail="close">‹</button>
<span class="dt-title">孕育资源库</span>
<!-- 原来是 🔍 emoji(各系统渲染成实物放大镜),换成和档案页一致的扁平线性图标 -->
<button class="dt-plain" data-search="1" aria-label="搜索"><svg viewBox="0 0 24 24" width="20" height="20"><circle cx="11" cy="11" r="6.5" fill="none" stroke="currentColor" stroke-width="1.9"/><path d="M16 16l4.5 4.5" stroke="currentColor" stroke-width="1.9" stroke-linecap="round"/></svg></button>
</div>
<div class="detail-body">
<!-- ================= 试用装福利 banner =================
排在最前面(在那句 rl-lead 之前):它是这一页最想被看见的东西。
⚠️ 和服务卡同样的理由,故意**不挂 data-open / data-modal**。
那条通用通道直接调 openModal,会绕过 openWx() —— 模式类名、
引导语、上次残留的红字和输入内容、还有 wxPending 都不会被重置。
⚠️ 里面这张示意图**绝不能带 .wx-qr 类**。冒烟测试有一条钉着
「全页只有一处二维码」,蹭了那个类名会当场变红 —— 而且它也确实
不是二维码。用独立的 .rlg-fig。
⚠️ 2026-09-19:兑换码闸门取消后不再有「已解锁」状态,所以这条 banner
**恒显示**(原来 syncUnlockCopy() 会按解锁态给它挂 .hide,已删除)。
换真图:把 .rlg-fig 整个 <svg> 换成一个 img,类名照旧写 rlg-fig,
CSS 一行不用动(.rlg-fig 同时管 svg 和 img)。
⚠️ 这里**不要**写出图片路径的字面量 —— build.js 的 ASSET_RE
连 HTML 注释一起扫,写了就当场报「缺少资源文件」。 -->
<button class="rl-gift ripple" id="rlGift">
<!-- ⚠️ 这条 banner 和「我的」页那张 .wx-card 是**同一件商品的同一张卡**,
所以这一版跟着改成同样的三段竖排:标题行 / 图文行 / 通栏 CTA。
用户上一批的原话是两处「一模一样」;只改一边,同一个商品在两个
页面上又会长成两个样子 —— 上一版商品名写岔(那边「可心柔试用装」、
这边「免费领可心柔试用装」)就是这么来的。
⚠️ 但**比例刻意比那边小一号**(图 68×60 不跟着放大到 88、字号各降一档)。
这不是偷懒:那边是「我的」页上的一张独立卡,四周是空的;
这条 banner 上面是标题栏、下面紧跟着 128 条资源列表,
照搬 88px 的图 + 22px 的价格会把整条撑到 200px 上下,
把真正的资源列表整个顶到首屏外 —— banner 抢了主内容的位置。
⚠️ 宽度账(320 屏):320 − 页边 44(.detail-body 左右各 22)
− 卡内边 28 = 248,和那边一样宽。
本批标题行改成**竖排**(跟 .wx-card 同步),所以「标题和限领抢
同一行」那笔账整个不存在了:
商品名独占一行:「可心柔婴儿纸巾」7 字 × 16 = 112 ≤ 248 ✓ 余 136px
「限领一份」另起一行:4 字 × 11.5 = 46 ✓
⚠️ 这边**本来就排得下**(上一版横排是 7×15 + gap 6 + 4×11.5 = 157
≤ 248,余 91px)。跟着改成竖排纯粹是为了两张卡保持同构 ——
真正被挤爆的是「我的」页那张:它的右列只有 160px。
所以别拿「这边明明放得下」当理由把这边改回横排,
那只会让同一件商品在两个页面上又长成两个样子。 -->
<span class="rlg-head">
<!-- 标题和「我的」页 #meWxTitle 保持**同一个字符串**「可心柔婴儿纸巾」。
测试里有一条拿同一个变量同时比对两处,写岔了会红。
⚠️ 和 .wx-title 一样:标题只放商品名,不许把括号那半句并进来。
.rlg-title 是 nowrap + ellipsis,并进来 16 字 ≈ 264px > 248,
会被省略号从「(」处齐根切掉,而 textContent 照样是全的。 -->
<span class="rlg-title">可心柔婴儿纸巾</span>
<!-- ⚠️ 「限领一份」只是文案,前端**不记状态**:真正的把关在顾问
那边人工做。前端记了也挡不住换设备/清缓存,不假装能管住。
别看到这行就以为漏了个 localStorage。
⚠️ 「(每人限领一份)」砍成「限领一份」是**第七批**的事,
**这边一直排得下** —— 砍它纯粹是为了和「我的」页 .wx-lim
保持逐字相同(用户早先要求两张卡「一模一样」,smoke 有断言钉着)。
当时被宽度逼的是那边:标题行横排、右列 160px、余量只剩 8px。
⚠️ 第八批那边也改成竖排了,8px 那笔账已经不存在,两边**都**放得下。
仍保持 4 字是因为用户没要求改字,不是因为放不下。
这两句必须**一起改**,只改一边会当场红。 -->
<span class="rlg-lim">限领一份</span>
</span>
<span class="rlg-body">
<!-- 「示例图 · 待替换」这几个字写在 svg **内部**:换成真图时它跟着
一起消失,不会剩一句「待替换」孤零零挂在真图旁边。 -->
<svg class="rlg-fig" viewBox="0 0 68 60" role="img" aria-label="示例图,待替换为可心柔婴儿纸巾实物图">
<rect x="6" y="14" width="56" height="34" rx="6" fill="#FFD9C6"/>
<rect x="6" y="14" width="56" height="12" rx="6" fill="#FFC3A8"/>
<path d="M20 26c0-5 4-9 9-9h10c5 0 9 4 9 9" fill="none" stroke="#E8724C" stroke-width="2.4"/>
<path d="M26 33h16M26 39h11" stroke="#E8724C" stroke-width="2.4" stroke-linecap="round"/>
<text x="34" y="57" text-anchor="middle" font-size="7" fill="#B0907F">示例图 · 待替换</text>
</svg>
<span class="rlg-main">
<!-- ⚠️ 角标从卡外边缘(position:absolute;top:-7px)挪进了右列顶部。
原因和「我的」页那张卡一样:.rl-gift 挂着 .ripple,
.ripple 的 overflow:hidden 把负偏移的角标裁掉一半,
「福利」两个字只露下半截。这是本项目里**最早**的那处裁切。
别改回绝对定位。 -->
<span class="rlg-badge">福利</span>
<!-- 价格行,和「我的」页 .wx-price 同一件事、同样只写到手价。
⚠️ 一样**不编划线价**:可心柔是真实品牌,划线价属于价格宣称。
「¥0」只是把这张卡一直写着的「立即免费领取」换个说法。 -->
<span class="rlg-price"><i>到手价</i><b>¥0</b></span>
<!-- ⚠️ 这行原来是写死的静态文本,没有小名。用户要求两处「一模一样」,
所以给了 id 并纳入 syncBabyWords() 一起刷 —— 否则改完小名,
「我的」页说「加小汤圆的孕育顾问领取」、这里说「加顾问领取」,
同一句话又变成两个说法。静态兜底同样写「宝宝的」。 -->
<span class="rlg-sub" id="rlgSub">加宝宝的孕育顾问领取</span>
</span>
</span>
<!-- 实心 CTA,现在是 .rl-gift 的直接子元素、通栏 248px
(上一版收在右列里只有 170px)。
原来这里只有一个 .rlg-chev「›」,尖括号在这一页的语义是
「还有下一层可以翻」,而这条 banner 是要人**领东西**。
⚠️ 「›」已删除:一张卡上同时摆按钮和尖括号 = 两个「点我」信号打架。
⚠️ 必须是 <span>:外层 .rl-gift 本身是 <button>,按钮套按钮非法。 -->
<span class="rlg-cta">立即免费领取</span>
</button>
<!-- 静态文案(原「还没解锁 / 已解锁」两版已随闸门一起取消)。
「首次领取需添加顾问微信」这句必须提前说:让人一路读到底、点了
「立即领取」才撞上二维码,是把条件藏到最后一步才亮出来。 -->
<p class="rl-lead"><b>128</b> 份资源按孕周持续更新 · 首次领取需添加顾问微信</p>
<div class="chips-row" id="resChips">
<button class="chip is-active" data-rescat="all">全部</button>
<button class="chip" data-rescat="产检">产检</button>
<button class="chip" data-rescat="营养">营养</button>
<button class="chip" data-rescat="胎教">胎教</button>
<button class="chip" data-rescat="运动">运动</button>
<button class="chip" data-rescat="心理">心理</button>
<!-- 收藏不是分类而是筛选,所以默认排在最后。但一旦用户有了收藏,
renderResList() 会把它挪到「全部」后面并带上数量 —— 常用的东西
不该让人横向滑到头才找得着。这里写的是「还没收藏过」时的样子。
⚠️ 原来的「✓ 已领」chip 已删除:9.19 需求取消了「立即领取即到手」
这个动作(资料由顾问在微信里发),H5 侧没有「已领」这个概念了。 -->
<button class="chip chip-fav" data-rescat="fav">★ 收藏</button>
</div>
<div class="res-list" id="resList"></div>
<p class="unlock" id="resEmptyHint"></p>
</div>
</section>
<!-- ================= 资源 内容页 ================= -->
<section class="detail" id="detailRes">
<div class="detail-bar">
<button class="dt-back" data-detail="close">‹</button>
<span class="dt-title">资源详情</span>
<button class="dt-star" id="resStar">☆</button>
</div>
<div class="detail-body">
<div class="rd-cover" id="rdCover"><img id="rdImg" src="assets/img/9f3fa8edf4aa.png" alt=""></div>
<span class="rd-cat" id="rdCat">产检</span>
<h1 class="rd-title" id="rdTitle">《糖耐检查全攻略》</h1>
<p class="rd-desc" id="rdDesc">孕24-28周必看 · 附检查前饮食清单</p>
<div class="rd-meta" id="rdMeta"></div>
<div class="rd-body" id="rdBody"></div>
<h3 class="d-sec">相关推荐</h3>
<div class="res-list slim" id="rdMore"></div>
</div>
<!-- 其他资源 / 立即领取吸底浮动:移出 .detail-body,怎么滑都跟着。
左边这颗原来是「★ 收藏」,和右上角星星功能完全重复(它就是转发点击给星星)。
收藏保留在星星上,这个位置让给「去看看别的」这个真正缺的出口。
文案用「其他资源」而不是「资源库」:用户此刻就在一份资源里,
「其他」说的是和手上这份的关系,比页面名更能说清点下去会看到什么。 -->
<div class="rd-cta" id="rdCta">
<button class="dl-btn ghost" id="rdLib">其他资源</button>
<button class="dl-btn main" id="rdGet">立即领取</button>
</div>
</section>
<!-- ================= 档案图片 预览页 =================
周档案 / 完整档案共用这一个壳。图是进来时用 canvas 现画的,
画完顺手存成 data URI —— 「保存图片」直接下载它,
「分享朋友圈」把它交给系统分享面板(不支持就提示长按图片),
「打印」走 window.print(),样式表里 @media print 只留这张图。 -->
<section class="detail" id="detailPoster">
<div class="detail-bar">
<button class="dt-back" data-detail="close">‹</button>
<span class="dt-title" id="pstBar">本周档案</span>
</div>
<div class="detail-body">
<!-- 抬头卡:图标 + 档案名 + 一句说明,比孤零零一行灰字有分量 -->
<div class="pst-hero">
<span class="pst-ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="24" height="24">
<path d="M7 3.5h7.5L19 8v12.5H7z" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
<path d="M14 3.5V8h5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/>
<path d="M10 12h6M10 15.5h6M10 19h3.5" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/>
</svg>
</span>
<div class="pst-h-txt">
<h2 class="pst-h-title" id="pstName">本周档案</h2>
<p class="pst-lead" id="pstLead">这一周的记录已经排好版了。</p>
</div>
</div>
<!-- 这张图里都装了什么,用几粒胶囊先交代清楚 -->
<div class="pst-chips" id="pstChips"></div>
<!-- 由 JS 往里塞 N 个 .pst-frame(白卡纸 + 一道虚线裁切位,像待打印的样张)。
本周档案是一张长图,完整档案是一页一张 A4 —— 屏幕上就按纸的样子一张张往下排。
画布都按 2 倍图给(750 宽),按 375 显示在手机上不糊。 -->
<div class="pst-pages" id="pstPages"></div>
<p class="pst-tip" id="pstTip">图片已生成。在微信里也可以长按上面这张图,直接保存或转发。</p>
</div>
<div class="rd-cta pst-cta" id="pstCta">
<!-- data-export 是委托用的:这三颗按钮不逐个绑监听,统一走 app.js 的
$$('[data-export]') 委托 → exportAct。id 还留着,
renderPoster() 要按「本周/完整」换这两颗的主次样式。 -->
<button class="dl-btn ghost" id="pstPrint" data-export="print">打印</button>
<button class="dl-btn ghost" id="pstSave" data-export="save">保存图片</button>
<button class="dl-btn main" id="pstShare" data-export="share">分享朋友圈</button>
</div>
</section>
<!-- ================= 设置与服务 二级页 =================
隐私与权限 / 帮助与反馈 / 关于兜爸心选 三项共用这一个壳,
内容由 js 里的 SETTINGS 表渲染进 #stBody,避免写四份几乎一样的结构 -->
<section class="detail" id="detailSetting">
<div class="detail-bar">
<button class="dt-back" data-detail="close">‹</button>
<span class="dt-title" id="stTitle">隐私与权限</span>
<button class="dt-plain" id="stMore" aria-label="更多">⋯</button>
</div>
<div class="detail-body">
<div id="stHero"></div>
<p class="st-lead" id="stLead"></p>
<div id="stBody"></div>
<p class="unlock" id="stFoot"></p>
</div>
</section>
<!-- ================= 孕期盲盒(2026-10-01,需求文档 v1.0 第三节) =================
上半是「开盒舞台」,下半是卡册(按孕早/中/晚期分三组)+ 规则与概率说明(合规:公示每档概率)。
.bx-reveal 是开盒后的翻卡层,盖在本页之上。全部渲染在 app.js 的 blindBox 段。 -->
<section class="detail bx" id="detailBox">
<div class="detail-bar">
<button class="dt-back" data-detail="close">‹</button>
<span class="dt-title">孕期盲盒</span>
<button class="dt-plain bx-rule-btn" id="bxRuleBtn" type="button">规则</button>
</div>
<div class="detail-body bx-body">
<div class="bx-stage" id="bxStage">
<span class="bx-rays" aria-hidden="true"></span>
<span class="bx-sp s1" aria-hidden="true">✦</span><span class="bx-sp s2" aria-hidden="true">✦</span><span class="bx-sp s3" aria-hidden="true">✦</span><span class="bx-sp s4" aria-hidden="true">✦</span>
<div class="bx-week" id="bxWeek"></div>
<div class="bx-hint" id="bxHint"></div>
<button class="bx-box" id="bxBox" type="button" aria-label="开启盲盒"></button>
<button class="bx-mini hide" id="bxMini" type="button" aria-label="查看这张宝宝卡"></button>
<span class="bx-shadow" aria-hidden="true"></span>
<button class="bx-open" id="bxOpen" type="button"></button>
<div class="bx-misc" id="bxMisc"></div>
</div>
<!-- 下周预告:下一孕周卡片的剪影 + 倒计时(营造等待感)。本周已开、且还没到 40 周时显示 -->
<div class="bx-next hide" id="bxNext">
<div class="bx-next-img" id="bxNextImg" aria-hidden="true"></div>
<div class="bx-next-t"><b id="bxNextW"></b><span id="bxNextS"></span></div>
<div class="bx-next-cd"><b id="bxNextD"></b><span>天后开启</span></div>
</div>
<!-- 待解锁(2026-10-01 价值感改造):参与之后漏开的周不再白送,完成一个孕期工具任务解锁一周。
任务是否完成由服务端查数胎动每日足迹核验(GET /blindbox 的 tasks),解锁 POST /blindbox/unlock -->
<div class="bx-lock hide" id="bxLock">
<div class="bx-lock-h"><span class="bx-lock-ico" aria-hidden="true">🔒</span>
<div><b>错过的 <em id="bxLockN"></em> 周待解锁</b><p>用一次孕期小工具,就能解锁一周补开</p></div></div>
<div class="bx-chips" id="bxLockChips"></div>
<div class="bx-tasks" id="bxTasks"></div>
</div>
<div class="bx-pend hide" id="bxPend">
<div class="bx-pend-t">还有 <b id="bxPendN"></b> 个盲盒等你打开 <em>错过的周会一直保留到孕 40 周</em></div>
<div class="bx-chips" id="bxChips"></div>
</div>
<section class="bx-album">
<div class="bx-ah">
<h3>我的宝宝卡册</h3>
<span class="bx-cnt"><b id="bxGot">0</b>/<span id="bxAll">37</span></span>
</div>
<div class="bx-prog"><i id="bxBar"></i></div>
<!-- 分段集齐奖励:孕早/中/晚期各一段 + 37 张全集。达成即记账,兜豆系统上线前由工作人员人工发放 -->
<div class="bx-goal" id="bxGoal"></div>
<p class="bx-anote" id="bxAnote"></p>
<div id="bxGroups"></div>
</section>
<details class="bx-rules" id="bxRules">
<summary>规则与概率说明</summary>
<div class="bx-rules-b" id="bxRulesBody"></div>
</details>
</div>
<div class="bx-reveal" id="bxReveal" aria-hidden="true">
<span class="bx-glow" aria-hidden="true"></span>
<span class="bx-flash" aria-hidden="true"></span>
<div class="bx-confetti" id="bxConfetti" aria-hidden="true"></div>
<div class="bx-rv-in">
<div class="bx-tag" id="bxTag"></div>
<div class="bx-persp">
<div class="bx-card3d" id="bxCard3d">
<div class="bx-face bx-back" id="bxBack"></div>
<div class="bx-face bx-front" id="bxFront"></div>
</div>
</div>
<p class="bx-dev" id="bxDev"></p>
<div class="bx-diary" id="bxDiary"></div>
<div class="bx-reward" id="bxReward"></div>
<button class="bx-keep" id="bxKeep" type="button"></button>
</div>
</div>
</section>
<!-- ================= 记一下 底部弹层 ================= -->
<div class="scrim" id="scrim"></div>
<section class="sheet" id="sheet">
<div class="sheet-grip" id="sheetGrip"><span></span></div>
<div class="sheet-head">
<div>
<h2>记一下</h2>
<p class="sk-sub" id="skSub"></p>
<!-- ⚠️ 临时隐藏(2026-09-22):「今天已记 N 条」依赖记录/统计功能,尚未实现 -->
<p class="tbd-hide"><span id="noteWhen">—</span> · 今天已记 <b id="todayCount">4</b> 条</p>
</div>
<button class="sheet-close" data-sheet="close" aria-label="关闭"><svg viewBox="0 0 16 16" width="12" height="12" aria-hidden="true"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg></button>
</div>
<div class="sheet-scroll">
<!-- ⚠️ 临时隐藏整块(2026-09-22):「今天已使用」快捷区 + 下方 .quick-row 的功能还没实现
(数据是写死的演示值 58.2kg / 待记录)。
**只挂 .tbd-hide,不要删 DOM**:JS 里 $('#quickKick') 没有判空,
还有 #kickCount / #todayCount / #noteWhen 会被回填,删掉会直接抛错。
恢复:去掉这三处 .tbd-hide 即可(含 <p> 那处),dedupeApps 会自动重新生效。 -->
<div class="apps-head first tbd-hide">今天已使用 <button class="sec-more" data-toast="点一下可直接补记">点一下直接记录 ›</button></div>
<div class="quick-row tbd-hide">
<button class="quick q1 ripple" id="quickKick"><img class="q-ico" src="assets/img/df905dcf3a1c.png" alt="" aria-hidden="true"><span class="q-t">数胎动</span><span class="q-v">+<b id="kickCount">1</b> 次</span></button>
<button class="quick q2 ripple" data-toast="已记录体重 58.2kg"><img class="q-ico" src="assets/img/a9e3b27a4a23.png" alt="" aria-hidden="true"><span class="q-t">孕期体重</span><span class="q-v">58.2kg</span></button>
<button class="quick q3 ripple" data-toast="开始测量胎心……"><img class="q-ico" src="assets/img/30de43bbc9aa.png" alt="" aria-hidden="true"><span class="q-t">胎心记录</span><span class="q-v">待记录</span></button>
<!-- 「今日心情」已按需求删除:记一下里只放工具,不放心情打卡。
删掉后 .quick-row 由 4 列改成 3 列,见 style.css -->
</div>
<!-- 2026-10-02 用户:「记一下弹窗有点单薄」→ 工具宫格上面加两层,数据都来自今日脚印接口(_fpData),不编数据:
① 今天卡:日期 + 孕周 + 一圈「今天记了几项」进度环 + 已记的工具小胶囊;
② 「先记这件」:今天还没用的前 2 个工具,配一句该做的事(todoLine),大卡可点。
接口没回来 / 访客 → 这两块保持隐藏,只剩副标题 + 宫格(和原来一样)。 -->
<!-- 2026-10-02 二改:用户要求弹窗别超过半屏 → 「今天卡」和「先记这件」合成一条:进度环 + 今天记了几项 + 下一件该做的事(去记),
已记的工具改成宫格图标上的小勾。整个弹窗 max-height 50%,工具多了宫格内部滚动。 -->
<div class="sk-today hide" id="skToday">
<div class="sk-ring" id="skRing" aria-hidden="true"><svg viewBox="0 0 44 44"><circle cx="22" cy="22" r="18"/><circle class="sk-arc" id="skArc" cx="22" cy="22" r="18"/></svg><b id="skNum">0</b></div>
<div class="sk-tt"><b id="skLine"></b><span id="skDone"></span></div>
<button class="sk-go hide" id="skGo" type="button">去记</button>
</div>
<div class="apps-head for-all tbd-hide">全部孕期工具 <button class="sec-more" data-toast="共 16 个孕期工具">共 16 个 ›</button></div>
<div class="apps-grid" id="appsGrid">
<button class="app ripple"><img class="a-ico" src="assets/img/505ace532176.png" alt="" aria-hidden="true">数胎动</button>
<button class="app ripple"><img class="a-ico" src="assets/img/8b0f0a53938b.png" alt="" aria-hidden="true">孕期体重</button>
<button class="app ripple"><img class="a-ico" src="assets/img/9a2e7fa472dc.png" alt="" aria-hidden="true">孕检日历</button>
<!-- ⚠️ 下面这十几枚是**接口数据到达前的静态占位**:loadPublishedApps() 拉到
/api/mp/apps 后会用 renderAppGrid() 整个 innerHTML 覆盖。所以这里改不改
只影响首帧闪现;本批把「生长曲线」同步改成「数宫缩」,与首页卡口径一致。 -->
<button class="app ripple"><img class="a-ico" src="assets/img/f989a64fe960.png" alt="" aria-hidden="true">数宫缩</button>
<button class="app ripple"><img class="a-ico" src="assets/img/fd875eb78785.png" alt="" aria-hidden="true">胎心记录</button>
<button class="app ripple"><img class="a-ico" src="assets/img/97c33531806b.png" alt="" aria-hidden="true">孕肚照</button>
<button class="app ripple"><img class="a-ico" src="assets/img/de0bc9e250f2.png" alt="" aria-hidden="true">B超影像</button>
<button class="app ripple"><img class="a-ico" src="assets/img/b855b45df1f3.png" alt="" aria-hidden="true">产检报告</button>
<button class="app ripple"><img class="a-ico" src="assets/img/c2ae6e98283b.png" alt="" aria-hidden="true">血压血糖</button>
<button class="app ripple"><img class="a-ico" src="assets/img/4657f50f42c8.png" alt="" aria-hidden="true">用药记录</button>
<button class="app ripple"><img class="a-ico" src="assets/img/72910df66ee3.png" alt="" aria-hidden="true">运动记录</button>
<button class="app ripple"><img class="a-ico" src="assets/img/dd5639fde61d.png" alt="" aria-hidden="true">饮食记录</button>
<button class="app ripple"><img class="a-ico" src="assets/img/f0d5e062f11a.png" alt="" aria-hidden="true">睡眠记录</button>
<button class="app ripple"><img class="a-ico" src="assets/img/056212807d86.png" alt="" aria-hidden="true">胎教音乐</button>
<button class="app ripple"><img class="a-ico" src="assets/img/7b7d81e02f42.png" alt="" aria-hidden="true">妈妈手记</button>
<button class="app ripple"><img class="a-ico" src="assets/img/f34fb7fdeea5.png" alt="" aria-hidden="true">更多工具</button>
</div>
</div>
</section>
<!-- ================= 搜索层 ================= -->
<section class="search-layer" id="searchLayer">
<div class="sl-bar">
<!-- ⚠️ 占位符里列的是可搜的东西。来信已整块移出本站(第三方 H5),
别再把它写回这句 —— 搜索框不该教用户搜一个界面上不存在的东西。
⚠️ 这句是 placeholder **属性**,不是文字节点 —— 只扫 textContent 的检查
抓不到它,改口时别忘了它。 -->
<div class="sl-input"><span>🔍</span><input id="searchInput" placeholder="搜索档案、记录……"></div>
<button class="sl-cancel" data-search="close">取消</button>
</div>
<div class="sl-body">
<div class="sl-sec">最近搜索</div>
<div class="chips-row wrap">
<button class="chip">第一次胎动</button><button class="chip">大排畸</button><button class="chip">体重</button><button class="chip">胎心</button>
</div>
<div class="sl-sec">大家都在搜</div>
<div class="sl-hot">
<button class="hot"><i>1</i>糖耐检查怎么准备</button>
<button class="hot"><i>2</i>孕<span data-wk="n">—</span>周胎动多少正常</button>
<button class="hot"><i>3</i>孕中期体重增长标准</button>
<button class="hot"><i>4</i>数胎动的正确方法</button>
</div>
</div>
</section>
<!-- ================= 通用弹窗(往期小结 / 企业微信 / 我的资料) ================= -->
<div class="modal-scrim" id="modalScrim"></div>
<!-- ================= 往期小结 =================
原来点往期列表是把 AI 小结页顶上的今日卡整个换掉,看完想回到「今天」没有出口。
改成在原页上浮一层:内容只读,看完点底下的叉就走。
叉放在最下面而不是右上角 —— 这一屏是一路往下读完的,
读完手指就停在底部,再让人抬回右上角去关不合理。 -->
<section class="modal" id="modalAiLog" role="dialog" aria-modal="true" aria-labelledby="alogTitle">
<div class="alog-head">
<span class="alog-week" id="alogWeek">孕24周+2</span>
<span class="alog-mood" id="alogMood">安心</span>
</div>
<h3 class="alog-date" id="alogTitle">9月2日 周一</h3>
<p class="alog-txt" id="alogTxt"></p>
<!-- 只留这组数(胎动 / 体重 / 睡眠)—— 这是当天从小程序里真取到的记录。
下面本来还跟着一组 1/2/3 的建议卡(.alog-tips),删了:
那是 AI 当天现写的话,隔了几天再翻出来看已经没有用了,
而且它和上面那句 alogTxt 说的是同一件事,等于把结论又铺开讲三遍。
往期该回答的只有一个问题——那天的数是多少。建议留在「今日小建议」里。 -->
<div class="ai-stats alog-stats" id="alogStats"></div>
<button class="modal-x modal-x-btm" data-modal="close" aria-label="关闭">
<svg viewBox="0 0 24 24" width="16" height="16" aria-hidden="true">
<path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/>
</svg>
</button>
</section>
<!-- ================= 百度网盘领取(已下线) =================
⚠️ 2026-09-19(9.19 需求 1.2 + 需求方确认):资料改由企业微信顾问**直接发放**,
H5 不再发网盘链接和提取码 —— 原来这个 #modalPan 弹窗、js 里的 PAN/PAN_DEFAULT
链接表、givePan()、「已领」标记已整块删除。要恢复「H5 自己发链接」,
得先回问需求方(那等于把「解锁」概念加回来)。
⚠️ 后端 /api/mp/public/mama-box/config 里的 pan / panDefault / wxCode 三个字段
也随之失效(box_app 侧后续清理)。 -->
<!-- ================= 加企业微信顾问 =================
⚠️ 2026-09-19(9.19 需求 1.1 / 1.2):这个弹窗从「兑换码闸门」简化成**纯提示**。
改动前后:
· 删掉兑换码输入框 + 「以后再说」「解锁全部资料」两颗按钮(需求 1.2);
· 原来的 is-gate / is-gift / is-done 三模式**合并成单态** —— 不再有「解锁」
这个状态,弹窗只负责把人引导到顾问那儿,资料由顾问在微信里直接发(需求方确认);
· 二维码下方文案改成需求指定的「请添加资料小助手,回复『领资料』」(需求 1.1)。
三个入口(资料详情「立即领取」、「我的」页服务卡、资源库试用装 banner)
共用这一个壳、同一句话,openWx() 不再按入口改写文案。
⚠️ data-autofocus="off":这个弹窗的主角是中间那张二维码,用户第一件事
是举起另一台手机去扫它。默认的自动聚焦会把输入法键盘顶起来,
正好盖住二维码下半截 —— 人得先把键盘收掉才能扫。
⚠️ 弹窗**没有底部按钮**:唯一出口是右上角 .modal-x(外加遮罩 / Esc)。
别把「优化掉没有按钮的头部」这种想法落到这颗叉上 —— 那会把人关在弹窗里。 -->
<section class="modal wx-modal" id="modalWx" data-autofocus="off"
role="dialog" aria-modal="true" aria-labelledby="wxTitle">
<div class="modal-head">
<!-- 兜底写「宝宝的」;填过小名的由 syncBabyWords() 刷成小名。
⚠️ 这行藏在弹窗里,改完小名不点开就看不见 —— 属于肉眼巡查
永远扫不到的挂点,只能靠 smoke 里那条断言守着。 -->
<h3 id="wxTitle">宝宝的孕育顾问</h3>
<button class="modal-x" data-modal="close" aria-label="关闭">
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true">
<path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/>
</svg>
</button>
</div>
<!-- 单态文案:说清「加顾问 → 说一句领资料 → 她直接发」,不要求用户填任何东西。
⚠️ 称呼是「顾问」(与标题一致);二维码下方那句按需求原文写「资料小助手」。
这一处不一致是需求方明确保留的(见改动清单 D3),别顺手「统一」。 -->
<p class="modal-lead" id="wxLead">扫码添加顾问,说一句「领资料」,她会把资料直接发给你。</p>
<div class="wx-qr-box">
<!-- 真实企业微信顾问二维码(2026-09-22 换上,替换掉原来的占位 svg)。
文件 assets/img/4834fe8d686a.png ,396×396 PNG,
md5 = 4834fe8d686a6ca129c8e51e87fe62f8(**前 12 位就是文件名**)。
⚠️ 换码时的几条规矩:
① 文件名取**文件内容 md5 的前 12 位**(md5sum 新图 | cut -c1-12)。
该目录下 35 张图全按这个规则,别起描述性名字 —— 内容一变 URL 就变,
天然不会被上一版二维码的缓存顶住。
② 只动下面这一行。.wx-qr 挂在元素本身、svg 与 img 通吃(见 mama-box.css),
CSS 一个字都不用改:168×168、14px 圆角都是现成的。
14px 圆角只吃掉四角极角的**白色静默区**(本图静默区 13/396 → 显示约 5.5px,
对角裁切 4.1px < 到黑色模块的 7.8px),切不到码点,不影响扫码。
③ 注释里除了上面那条**真实存在**的路径,别再写别的图片路径字面量 ——
构建脚本连 HTML 注释一起扫,写了不存在的就报「缺少资源文件」。 -->
<img class="wx-qr" src="assets/img/4834fe8d686a.png" alt="企业微信顾问二维码">
<!-- 需求 1.1 指定的文案,逐字照抄(直角引号沿用站内习惯) -->
<p class="wx-qr-cap">请添加资料小助手,回复「领资料」</p>
</div>
</section>
<!-- ================= 我的资料(妈妈 + 宝宝,可换头像) =================
⚠️ 这里原来有**两个**弹窗:这张「我的资料」(头像 / 昵称 / 一句话签名)
和它上面一张 #modalPreg「编辑孕期信息」(小名 / 性别 / 预产期 / 孕周)。
本批按用户要求「我的资料里加上宝宝小名、性别、预产期」,
做法是把 #modalPreg **整个并进来**,而不是把三个字段抄一份过来。
为什么不抄一份:抄完这三个字段就有两套输入框写同一份 state。
两套控件迟早会漂 —— 一边加了校验另一边没加、一边保存后调了
syncPregnancy() 另一边忘了调,而且这种不一致在两个弹窗**分别**
打开时都看不出来,只有用户在 A 改完去 B 看才暴露。
合并之后 pgName/pgSex/pgDue/pgWeek/pgDay 这五个 id 一个没改,
原来挂在它们身上的联动(改预产期→算孕周、改孕周→反推预产期)
原样继续生效,只是保存按钮从 #pgSave 并进了 #pfSave。
⚠️ 「孕期信息」那一节的「编辑 ›」和整张孕期卡现在都指向这个弹窗
(data-modal 从 modalPreg 改成了 modalProfile),别再找 modalPreg 了。
⚠️ 高度预算(.modal 是 max-height:92% + overflow-y:auto,撑爆不会破版、
只会要用户滚一下才够得着「保存」,但那已经算体验问题了):
22 内边 + 30 头 + 头像块 121 + 昵称/小名同行 78 + 性别 77
+ 预产期 78 + 孕周行 78 + 提示 43 + 底部按钮 68 + 20 内边 ≈ 615px
设计稿机身 932px × 92% = 857 ✓;最窄的 iPhone SE 667 × 92% = 613 ——
**贴着线**。所以下面做了两件省高度的事,别随手退回去:
① 昵称和宝宝小名并成一行 .fld-row(省 78px);
② 这个弹窗里的大头像从 92px 收到 76px(见 css 的 #modalProfile .av-big)。
再往里加字段之前,先回来把这笔账重算一遍。 -->
<!-- 盲盒 · 集齐全集的实物奖励:收货信息。POST /api/mp/public/mama-box/blindbox/claim -->
<section class="modal" id="modalBxAddr" role="dialog" aria-modal="true" aria-labelledby="modalBxAddrTitle">
<div class="modal-head">
<h3 id="modalBxAddrTitle">领取孕期纪念礼</h3>
<button class="modal-x" data-modal="close" aria-label="关闭">
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
</button>
</div>
<p class="bx-addr-tip">恭喜集齐 37 张宝宝卡!填好收货信息,工作人员会在 7 个工作日内为你寄出。</p>
<div class="fld-row">
<label class="fld"><span>收货人</span><input id="bxAddrName" type="text" maxlength="20" placeholder="姓名"></label>
<label class="fld"><span>手机号</span><input id="bxAddrPhone" type="tel" maxlength="20" placeholder="手机号"></label>
</div>
<label class="fld"><span>收货地址</span><input id="bxAddrAddr" type="text" maxlength="120" placeholder="省市区 + 详细地址"></label>
<div class="modal-foot"><button class="dl-btn main" id="bxAddrSave">提交</button></div>
</section>
<!-- 星座宝宝(2026-10-04):点头图孕囊旁的「xx宝宝」小牌打开(js/hero-fx.js 派发 mb-zodiac → app.js 填内容 + openModal)。
幸运色 → 性格 → 适合的职业;预产期落在星座交界时顶部给两个可切换的小标签。内容是星座小趣味,底部写明。 -->
<section class="modal zd-modal" id="modalZodiac" data-autofocus="off" role="dialog" aria-modal="true" aria-labelledby="zdName">
<!-- 2026-10-04 二版(用户:「弹出的页面重新设计,要有针对星座的设计感,背景色按火象、水象这些」):
顶部一条按四象配色的头带(火 / 土 / 风 / 水),右上角淡淡画出这个星座的星图(星星会闪),
正中玻璃圆里是星座符号;头带底边是元素纹样(火焰 / 山脊 / 云 / 水波),最下一层用正文底色填,头带「长」进正文。
素材在 js/zodiac-art.js。 -->
<div class="zd-band" id="zdBand">
<svg class="zd-sky" id="zdSky" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet" aria-hidden="true"></svg>
<button class="modal-x" data-modal="close" aria-label="关闭">
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
</button>
<span class="zd-glyph" id="zdGlyph" aria-hidden="true"></span>
<h3 id="zdName"></h3>
<p class="zd-range"><span class="zd-el" id="zdEl"></span><span id="zdRange"></span></p>
<div class="zd-switch" id="zdSwitch" hidden></div>
<svg class="zd-motif" id="zdMotif" viewBox="0 0 375 56" preserveAspectRatio="none" aria-hidden="true"></svg>
</div>
<p class="zd-words" id="zdWords"></p>
<div class="zd-sec zd-color">
<i class="zd-sw" aria-hidden="true"></i>
<div><span class="zd-label">宝宝幸运色</span><b id="zdColor"></b><p id="zdColorTip"></p></div>
</div>
<div class="zd-sec">
<span class="zd-label">宝宝的性格</span>
<div class="zd-tags" id="zdTraits"></div>
<p class="zd-para" id="zdPara"></p>
</div>
<div class="zd-sec">
<span class="zd-label">长大后可能会喜欢的职业</span>
<div class="zd-jobs" id="zdJobs"></div>
</div>
<p class="zd-foot">星座小趣味,听听就好 —— 每个宝宝都是独一无二的小宇宙</p>
</section>
<!-- 意见反馈(2026-10-04)。类型 → 描述 → 截图(选填,最多 3 张,走 /api/upload)→ 联系方式(选填)→ 提交。
底部「我提交过的」列出最近几条和处理状态 / 回复(运营在管理端 PUT /api/mama-box/feedbacks/{id} 标记)。 -->
<section class="modal fb-modal" id="modalFeedback" data-autofocus="off" role="dialog" aria-modal="true" aria-labelledby="fbTitle">
<div class="modal-head">
<h3 id="fbTitle">意见反馈</h3>
<button class="modal-x" data-modal="close" aria-label="关闭">
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
</button>
</div>
<p class="fb-lead">哪里不好用、想要什么新功能,都可以告诉我们,每一条都会认真看。</p>
<div class="fb-kinds" id="fbKinds" role="radiogroup" aria-label="反馈类型">
<button type="button" class="fb-kind" data-kind="bug" role="radio" aria-checked="false">功能问题</button>
<button type="button" class="fb-kind on" data-kind="idea" role="radio" aria-checked="true">功能建议</button>
<button type="button" class="fb-kind" data-kind="other" role="radio" aria-checked="false">其他</button>
</div>
<div class="fb-ta-wrap">
<textarea class="fb-ta" id="fbText" maxlength="500" placeholder="说说你遇到的问题或想法…"></textarea>
<span class="fb-count" id="fbCount">0/500</span>
</div>
<div class="fb-imgs" id="fbImgs">
<label class="fb-add" id="fbAdd"><input type="file" accept="image/*" id="fbFile" hidden><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true"><path d="M12 6v12M6 12h12" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg><span>截图</span></label>
<span class="fb-imgs-tip">选填,最多 3 张</span>
</div>
<label class="fld fb-contact"><span>联系方式(选填)</span><input id="fbContact" type="text" maxlength="40" placeholder="手机号或微信号,方便我们回复你"></label>
<div class="modal-foot"><button class="dl-btn main" id="fbSubmit">提交</button></div>
<div class="fb-mine" id="fbMine" hidden>
<h4>我提交过的</h4>
<div id="fbMineList"></div>
</div>
</section>
<!-- 同周悄悄话 · 全屏页(2026-10-02 二改)。「深夜星河」:深靛紫夜空 + 星点,悄悄话是一盏盏漂浮的玻璃泡,
抱抱越多越亮越大;最上面是今晚抱抱最多的一句,大字。底部一颗发光的「说一句悄悄话」。
层级 z 110:盖住 tabbar(60),低于说一句弹窗(modal 120+),所以在这页里点「说一句」弹窗能盖在上面。 -->
<section class="wh-page" id="whPage" role="dialog" aria-modal="true" aria-labelledby="whpTitle" aria-hidden="true">
<div class="whp-sky" aria-hidden="true"><i class="s1"></i><i class="s2"></i><i class="s3"></i><i class="moon"></i></div>
<!-- 2026-10-02 五改(用户:「打开页面夜空里特别多星星;点开一颗,它像流星一样滑落,白色的头往下滑,
尾巴就是这句话的字;先保证看清,刚开始慢一点,说完了唰地就快了;同时把对应内容呈现出来」):
夜空 = 每句一颗可点的亮星 + 一百多颗装饰小星(偶尔有无字的小流星划过);点亮星 → 流星 + 底部内容卡。 -->
<div class="whv-aura" id="whvAura" aria-hidden="true"><i></i><i></i></div>
<div class="wsk" id="wsk">
<div class="wsk-deco" id="wskStars" aria-hidden="true"></div>
<i class="wsk-shoot" aria-hidden="true"></i><i class="wsk-shoot s2" aria-hidden="true"></i>
<div class="wsk-bright" id="wskBright" role="group" aria-label="夜空里的星星,每颗是一句悄悄话"></div>
</div>
<header class="whv-top">
<button class="whp-back" id="whpBack" type="button" aria-label="返回">
<svg viewBox="0 0 24 24" width="20" height="20"><path d="M15 5l-7 7 7 7" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<span class="whp-tag" id="whpTag"></span>
<button class="whv-snd" id="whvSnd" type="button" aria-label="关闭声音"><svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 9.5h3.5L12 5.5v13l-4.5-4H4z" fill="currentColor" stroke="none"/><path class="on" d="M15.5 9a4 4 0 010 6M18 6.5a7.5 7.5 0 010 11"/><path class="off" d="M16 9.5l5 5M21 9.5l-5 5"/></svg></button>
<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>
<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>
<div class="whd" id="whDrawer" aria-hidden="true">
<div class="whd-panel" role="dialog" aria-label="全部悄悄话">
<div class="whd-head"><b>全部悄悄话</b><span id="whpCount"></span><button type="button" class="whd-x" id="whdX" 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="whp-list" id="whpList"></div>
</div>
</div>
<div class="whp-bar">
<button class="whp-say" id="whSay" type="button"><span>说一句悄悄话</span></button>
<p class="whp-left" id="whpLeft"></p>
</div>
</section>
<!-- 此刻 · 说一句悄悄话(2026-10-02)。预设句点一下就填进输入框;自由输入由服务端过微信内容安全审核 -->
<section class="modal wh-modal" id="modalWhisper" data-autofocus="off" role="dialog" aria-modal="true" aria-labelledby="whMTitle">
<div class="modal-head">
<h3 id="whMTitle">说一句悄悄话</h3>
<button class="modal-x" data-modal="close" aria-label="关闭">
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
</button>
</div>
<p class="wh-m-topic" id="whMTopic" style="display:none"></p>
<p class="wh-m-lead" id="whMLead">不显示昵称和头像,只有同孕期的妈妈能看到。她们会给你一个抱抱。</p>
<div class="wh-ta-box">
<textarea class="wh-ta" id="whTa" maxlength="40" rows="2" placeholder="今天想对谁说点什么?"></textarea>
<span class="wh-cnt" id="whCnt">0/40</span>
</div>
<div class="wh-m-sub">不知道说什么?点一句</div>
<div class="wh-presets" id="whPresets"></div>
<div class="modal-foot">
<button class="dl-btn main" id="whSend" type="button">悄悄发出</button>
<p class="wh-left" id="whLeft"></p>
</div>
</section>
<!-- 盲盒 · 分段集齐达成的庆祝弹层(内容由 app.js showPrize() 填) -->
<section class="modal bx-prize" id="modalBxPrize" role="dialog" aria-modal="true" aria-labelledby="bxPrizeT">
<div class="bx-prize-ico" aria-hidden="true">🏆</div>
<h3 id="bxPrizeT"></h3>
<p id="bxPrizeP"></p>
<div class="modal-foot"><button class="dl-btn main" id="bxPrizeOk">太好了</button></div>
</section>
<section class="modal pf-modal" id="modalProfile" data-autofocus="off" role="dialog" aria-modal="true" aria-labelledby="modalProfileTitle">
<div class="modal-head">
<h3 id="modalProfileTitle">我的资料</h3>
<button class="modal-x" data-modal="close" aria-label="关闭">
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true">
<path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/>
</svg>
</button>
</div>
<!-- 2026-10-01 资料弹窗改版(用户:「现在填写比较困难,让用户很轻松地就能填写完」):
① 顶部一条实时结果「孕 N 周 +D 天 · 还有 X 天见面」—— 填的每一步都立刻看到算出了什么;
② 预产期三种填法(分段切换):知道预产期 / 按当前孕周(大号 −/+ 步进,不用敲键盘)/ 按末次月经;
三种最后都落到 #pgDue(唯一的数据源),保存逻辑一行没改;
③ 头像缩到昵称左边,和昵称同一行;宝宝小名下面给几个一点即填的小名;
④ 弹窗不再自动聚焦昵称(data-autofocus="off")—— 一打开就弹键盘,正好盖住要填的预产期。 -->
<p class="pf-lead">填好预产期,孕周和倒计时都会自动算好</p>
<div class="pf-sum" id="pgSum">
<span class="ps-ico" aria-hidden="true">
<svg viewBox="0 0 24 24" width="22" height="22"><path d="M12 20.5s-7.5-4.4-7.5-10A4.3 4.3 0 0 1 12 8a4.3 4.3 0 0 1 7.5 2.5c0 5.6-7.5 10-7.5 10z" fill="currentColor"/></svg>
</span>
<span class="ps-main"><b id="pgSumT">还没填预产期</b><span id="pgSumS">选一种方式填一下,马上就好</span></span>
</div>
<div class="pf-card">
<div class="pf-ct">孕期信息<em>必填</em></div>
<div class="seg" id="pgMode" role="tablist">
<button type="button" class="seg-b is-active" data-mode="due">知道预产期</button>
<button type="button" class="seg-b" data-mode="week">按当前孕周</button>
<button type="button" class="seg-b" data-mode="lmp">按末次月经</button>
</div>
<div class="pg-pane is-on" data-pane="due">
<label class="date-btn">
<span class="db-ico" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20"><rect x="4" y="5.5" width="16" height="14.5" rx="3" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M4 10h16M8.5 3.5v4M15.5 3.5v4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></span>
<span class="db-txt" id="pgDueTxt">点这里选预产期</span>
<i class="chev">›</i>
<input id="pgDue" type="date" aria-label="预产期">
</label>
<p class="pg-tip">产检单 / B 超单上写的「预产期」</p>
</div>
<div class="pg-pane" data-pane="week">
<div class="stp-row">
<div class="stp">
<span class="stp-l">孕周</span>
<div class="stp-ctl">
<button type="button" class="stp-b" data-stp="w" data-d="-1" aria-label="孕周减一">−</button>
<input id="pgWeek" type="number" inputmode="numeric" min="0" max="42" placeholder="—">
<button type="button" class="stp-b" data-stp="w" data-d="1" aria-label="孕周加一">+</button>
</div>
</div>
<div class="stp">
<span class="stp-l">又几天</span>
<div class="stp-ctl">
<button type="button" class="stp-b" data-stp="d" data-d="-1" aria-label="天数减一">−</button>
<input id="pgDay" type="number" inputmode="numeric" min="0" max="6" placeholder="—">
<button type="button" class="stp-b" data-stp="d" data-d="1" aria-label="天数加一">+</button>
</div>
</div>
</div>
<p class="pg-tip" id="pgHint">比如「孕 24 周 +3 天」,就选 24 和 3</p>
</div>
<div class="pg-pane" data-pane="lmp">
<label class="date-btn">
<span class="db-ico" aria-hidden="true"><svg viewBox="0 0 24 24" width="20" height="20"><rect x="4" y="5.5" width="16" height="14.5" rx="3" fill="none" stroke="currentColor" stroke-width="1.8"/><path d="M4 10h16M8.5 3.5v4M15.5 3.5v4" stroke="currentColor" stroke-width="1.8" stroke-linecap="round"/></svg></span>
<span class="db-txt" id="pgLmpTxt">点这里选末次月经第一天</span>
<i class="chev">›</i>
<input id="pgLmp" type="date" aria-label="末次月经第一天">
</label>
<p class="pg-tip">按末次月经第一天 + 280 天推算预产期</p>
</div>
</div>
<div class="pf-card">
<div class="pf-ct">宝宝</div>
<label class="fld pf-fld"><span>小名</span><input id="pgName" type="text" maxlength="12" placeholder="给宝宝起个小名"></label>
<div class="nm-chips" id="pgNameChips">
<button type="button" class="nm-c">小汤圆</button><button type="button" class="nm-c">豆豆</button><button type="button" class="nm-c">糖糖</button><button type="button" class="nm-c">小米粒</button><button type="button" class="nm-c">乐乐</button>
</div>
<div class="fld pf-fld">
<span>性别</span>
<div class="pick-row" id="pgSex">
<button type="button" class="pick is-active" data-sex="unknown">还不知道</button>
<button type="button" class="pick" data-sex="boy">男孩</button>
<button type="button" class="pick" data-sex="girl">女孩</button>
</div>
</div>
</div>
<div class="pf-card">
<div class="pf-ct">妈妈</div>
<div class="pf-me">
<div class="av-pick">
<button class="av-big" id="avPick" aria-label="更换头像">
<svg viewBox="0 0 40 40" aria-hidden="true">
<circle cx="20" cy="14.5" r="7" fill="#fff"/>
<path d="M6.5 35c0-7.2 6-11.5 13.5-11.5S33.5 27.8 33.5 35z" fill="#fff"/>
</svg>
<img id="avPreview" alt="">
<span class="av-cam">
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true">
<path d="M3 8.6A2.6 2.6 0 0 1 5.6 6h1.9l1.3-2h6.4L16.5 6h1.9A2.6 2.6 0 0 1 21 8.6v8.8a2.6 2.6 0 0 1-2.6 2.6H5.6A2.6 2.6 0 0 1 3 17.4z" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linejoin="round"/>
<circle cx="12" cy="13" r="3.6" fill="none" stroke="currentColor" stroke-width="1.9"/>
</svg>
</span>
</button>
<input type="file" id="avFile" accept="image/*" class="av-file">
<!-- file input 藏起来但不能 display:none,否则部分安卓 WebView 点不动(0 尺寸 + opacity) -->
<input type="file" id="avFile" accept="image/*" class="av-file">
</div>
<label class="fld pf-fld"><span>怎么称呼你</span><input id="pfName" type="text" maxlength="16" placeholder="妈妈"></label>
</div>
<p class="pg-tip">点头像换张照片</p>
</div>
<div class="modal-foot pf-foot">
<button class="dl-btn ghost" data-modal="close">取消</button>
<button class="dl-btn main" id="pfSave">保存</button>
</div>
</section>
<div class="toast" id="toast"></div>
</div>
<!-- 微信 JS-SDK:仅在 web-view 内给 wx.miniProgram 用(「进商城」跳原生页)。
加载失败/离线时 window.wx 为 undefined,goMall() 会降级为 toast 提示,
不影响应用其余功能。 -->
<script src="js/zodiac-art.js"></script>
<script src="js/app.js"></script>
<!-- 2026-10-03 首页彩蛋:宝宝在肚皮底下动(独立文件,删掉这一行即整体下线;开关在「隐私权限」) -->
<script src="js/bump.js"></script>
<!-- 2026-10-04 首页头图:按孕周换宝宝 + 阳光水波(独立文件) -->
<script src="js/hero-fx.js"></script>
</body>
</html>