首页兜豆模块加横滑可兑好物;我的页孕期卡与兜豆卡对调;资料弹窗改版(实时结果条、预产期三种填法、孕周步进、小名建议)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
225
index.html
225
index.html
@@ -7,7 +7,7 @@
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<title>兜爸心选 · 孕育陪伴</title>
|
||||
<!-- V:26100137 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
|
||||
<!-- V:26100138 缓存版本标记:改 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 第四节) -->
|
||||
@@ -355,13 +355,17 @@
|
||||
<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):紧跟婴儿房(那张卡写着「攒兜豆把它布置好」),
|
||||
顺着「兜豆」这条线往下接。点击同「我的」页 #meExchCard → openExchange()。 -->
|
||||
<button class="exch-strip ripple" id="homeExch">
|
||||
<span class="es-coin" aria-hidden="true"><svg viewBox="0 0 24 24" width="36" height="36"><ellipse cx="12" cy="12.5" rx="7.4" ry="8.6" fill="#FFB5C4" transform="rotate(-18 12 12.5)"/><path d="M13.8 6.6c-2.6 1.6-3.4 5.8-1.2 9.4" stroke="#fff" stroke-width="1.6" fill="none" stroke-linecap="round" opacity=".85"/><path d="M12 15.6c-1.9-1.1-2.9-2.2-2.9-3.2a1.4 1.4 0 0 1 2.9-.5 1.4 1.4 0 0 1 2.9.5c0 1-1 2.1-2.9 3.2z" fill="#F0607E"/></svg></span>
|
||||
<!-- 首页 · 兜豆兑换商城模块(2026-10-01):紧跟婴儿房(那张卡写着「攒兜豆把它布置好」),
|
||||
顺着「兜豆」这条线往下接。头部一条 + 下面一排可兑好物;点哪儿都 openExchange()。 -->
|
||||
<section class="exch-mod" id="homeExchMod">
|
||||
<button class="exch-strip ripple" id="homeExch">
|
||||
<span class="es-coin" aria-hidden="true"><svg viewBox="0 0 24 24" width="36" height="36"><ellipse cx="12" cy="12.5" rx="7.4" ry="8.6" fill="#FFB5C4" transform="rotate(-18 12 12.5)"/><path d="M13.8 6.6c-2.6 1.6-3.4 5.8-1.2 9.4" stroke="#fff" stroke-width="1.6" fill="none" stroke-linecap="round" opacity=".85"/><path d="M12 15.6c-1.9-1.1-2.9-2.2-2.9-3.2a1.4 1.4 0 0 1 2.9-.5 1.4 1.4 0 0 1 2.9.5c0 1-1 2.1-2.9 3.2z" fill="#F0607E"/></svg></span>
|
||||
<span class="es-main"><b>兜豆兑好物</b><span id="homeExchSub">攒兜豆,好物免费兑</span></span>
|
||||
<span class="es-go">去兑换<i>›</i></span>
|
||||
</button>
|
||||
<span class="es-go">去兑换<i>›</i></span>
|
||||
</button>
|
||||
<!-- 本场可兑好物(/mall/drop:有库存且已开售,按 heat_30m 降序取前 6),横向滑动;拉不到数据整行隐藏 -->
|
||||
<div class="ex-goods hide" id="homeExchGoods"></div>
|
||||
</section>
|
||||
|
||||
<!-- 模块二 · 今天的动线。
|
||||
上一版这里是两个各自独立的模块:「AI今日小结」一张渐变卡,
|
||||
@@ -918,34 +922,6 @@
|
||||
</div>
|
||||
|
||||
<div class="page-body no-top">
|
||||
<!-- 兜豆兑换商城主卡(2026-10-01):半压在头部渐变边缘,是「我的」页最跳的一张。
|
||||
点击走 openExchange()(app.js)—— 链接优先取应用后台,取不到再用写死的兜底地址。
|
||||
#meExchSub 未登录写「攒兜豆,好物免费兑」,拿到余额后改成「兜豆 N · 可兑 M 件」(loadDou())。 -->
|
||||
<button class="bean-card ripple" id="meExchCard">
|
||||
<span class="bc-pattern" aria-hidden="true"></span>
|
||||
<span class="bc-top">
|
||||
<span class="bc-coin" aria-hidden="true"><svg viewBox="0 0 24 24" width="58" height="58"><ellipse cx="12" cy="12.5" rx="7.4" ry="8.6" fill="#FFB5C4" transform="rotate(-18 12 12.5)"/><path d="M13.8 6.6c-2.6 1.6-3.4 5.8-1.2 9.4" stroke="#fff" stroke-width="1.6" fill="none" stroke-linecap="round" opacity=".85"/><path d="M12 15.6c-1.9-1.1-2.9-2.2-2.9-3.2a1.4 1.4 0 0 1 2.9-.5 1.4 1.4 0 0 1 2.9.5c0 1-1 2.1-2.9 3.2z" fill="#F0607E"/></svg></span>
|
||||
<span class="bc-main">
|
||||
<span class="bc-kick">兜豆兑换</span>
|
||||
<span class="bc-title">兜豆兑好物</span>
|
||||
<span class="bc-sub" id="meExchSub">攒兜豆,好物免费兑</span>
|
||||
</span>
|
||||
<span class="bc-cta">去兑换<i>›</i></span>
|
||||
</span>
|
||||
<!-- 本场热门好物(/mall/drop,按 heat_30m 取前 3);拉不到数据就整行隐藏 -->
|
||||
<span class="bc-drop hide" id="meExchDrop"><span class="bd-head" id="meExchDropT"></span><span class="bd-goods" id="meExchGoods"></span></span>
|
||||
</button>
|
||||
|
||||
<!-- 授权完善资料: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>
|
||||
|
||||
<!-- 退出登录已移到个人资料卡右上角的图标按钮(#meLogoutBtn) -->
|
||||
<!-- ⚠️ 这两处的 data-modal 从 modalPreg 改成了 modalProfile:
|
||||
「编辑孕期信息」那张弹窗已整个并进「我的资料」(见文件末尾那段说明)。
|
||||
@@ -958,7 +934,9 @@
|
||||
宽度账(.page-body 左右各 18 → 可用 284,.sec-title 是 flex、gap 8):
|
||||
竖条 4 + 8 + 「孕期信息」4×17.5=70 + 8 + 「(必填)」4×12=48 + 8 + 「编辑 ›」≈34
|
||||
= 180 ≤ 284 ✓,余 104px,宽裕。 -->
|
||||
<h2 class="sec-title dark">孕期信息<span class="sec-req">(必填)</span> <button class="sec-more" data-modal="modalProfile">编辑 ›</button></h2>
|
||||
<!-- 2026-10-01:按用户要求孕期卡和兜豆卡对调 —— 孕期卡现在半压在头部底边上,所以去掉了上方的
|
||||
「孕期信息(必填)· 编辑 ›」标题行(标题压在渐变上很乱);整卡本身就点开资料弹窗,
|
||||
「必填」挪进弹窗里那张「孕期信息」卡的角标。 -->
|
||||
<button class="preg-card ripple" data-modal="modalProfile">
|
||||
<div class="pc-row">
|
||||
<!-- 孕周进度环 + 蜷着的宝宝。
|
||||
@@ -1002,6 +980,38 @@
|
||||
<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>
|
||||
|
||||
<h2 class="sec-title dark">兜豆兑换</h2>
|
||||
<button class="bean-card ripple" id="meExchCard">
|
||||
<span class="bc-pattern" aria-hidden="true"></span>
|
||||
<span class="bc-top">
|
||||
<span class="bc-coin" aria-hidden="true"><svg viewBox="0 0 24 24" width="58" height="58"><ellipse cx="12" cy="12.5" rx="7.4" ry="8.6" fill="#FFB5C4" transform="rotate(-18 12 12.5)"/><path d="M13.8 6.6c-2.6 1.6-3.4 5.8-1.2 9.4" stroke="#fff" stroke-width="1.6" fill="none" stroke-linecap="round" opacity=".85"/><path d="M12 15.6c-1.9-1.1-2.9-2.2-2.9-3.2a1.4 1.4 0 0 1 2.9-.5 1.4 1.4 0 0 1 2.9.5c0 1-1 2.1-2.9 3.2z" fill="#F0607E"/></svg></span>
|
||||
<span class="bc-main">
|
||||
<span class="bc-kick">兜豆兑换</span>
|
||||
<span class="bc-title">兜豆兑好物</span>
|
||||
<span class="bc-sub" id="meExchSub">攒兜豆,好物免费兑</span>
|
||||
</span>
|
||||
<span class="bc-cta">去兑换<i>›</i></span>
|
||||
</span>
|
||||
<!-- 本场热门好物(/mall/drop,按 heat_30m 取前 3);拉不到数据就整行隐藏 -->
|
||||
<span class="bc-drop hide" id="meExchDrop"><span class="bd-head" id="meExchDropT"></span><span class="bd-goods" id="meExchGoods"></span></span>
|
||||
</button>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- ================= 孕育顾问(企业微信入口) =================
|
||||
常驻在孕期卡下面:上面那张卡讲「你走到哪儿了」,这张接着说
|
||||
「路上有人陪你」,是同一段话的下一句;而「设置与服务」那组是
|
||||
@@ -1757,7 +1767,7 @@
|
||||
<div class="modal-foot"><button class="dl-btn main" id="bxPrizeOk">太好了</button></div>
|
||||
</section>
|
||||
|
||||
<section class="modal" id="modalProfile" role="dialog" aria-modal="true" aria-labelledby="modalProfileTitle">
|
||||
<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="关闭">
|
||||
@@ -1767,10 +1777,93 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 头像:点圆形区域直接唤起系统相册;file input 藏起来但不能 display:none,
|
||||
否则部分安卓 WebView 点不动,所以用 0 尺寸 + opacity 的写法 -->
|
||||
<div class="av-pick">
|
||||
<button class="av-big" id="avPick" aria-label="更换头像">
|
||||
<!-- 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"/>
|
||||
@@ -1784,52 +1877,16 @@
|
||||
</span>
|
||||
</button>
|
||||
<input type="file" id="avFile" accept="image/*" class="av-file">
|
||||
<div class="av-tip">点一下换张照片 · 只存在你自己的手机上</div>
|
||||
</div>
|
||||
|
||||
<!-- ⚠️ 这里原来第二格是「一句话签名」#pfBio,本批按用户要求**删掉了**,别加回来。
|
||||
连带删的还有「我的」页头像旁那行 #meBio 和 js 里的 syncBio() ——
|
||||
必须一起走:只删输入框、留着展示行,那行就变成一句谁也改不动的死文案,
|
||||
比没有更糟(用户会在资料页里翻来翻去找哪儿能改它)。
|
||||
|
||||
⚠️ 昵称(妈妈)和宝宝小名并成一行,是上面高度预算里省下的那 78px。
|
||||
两格都是短文本,半行宽装得下:.fld input 左右内边 15×2 = 30,
|
||||
半行宽 = (弹窗内容宽 - gap 12) / 2 ≈ (386-12)/2 = 187,
|
||||
可写 157px ÷ 14px 字 ≈ 11 个汉字,而 maxlength 分别是 16 / 12 ——
|
||||
填满会在框内横向滚动,不会破版。这是可接受的,别为此把它拆回两行。 -->
|
||||
<div class="fld-row">
|
||||
<!-- ⚠️ 不写死演示默认值(原 value="小满妈妈"/"小汤圆"已删):打开弹窗时由
|
||||
fillProfileForm() 按服务端档案回填,新用户全空 + placeholder 提示 -->
|
||||
<label class="fld"><span>昵称</span><input id="pfName" type="text" maxlength="16" placeholder="妈妈"></label>
|
||||
<label class="fld"><span>宝宝小名</span><input id="pgName" type="text" maxlength="12" placeholder="宝宝"></label>
|
||||
</div>
|
||||
<div class="fld">
|
||||
<span>宝宝性别</span>
|
||||
<!-- 性别会随档案一起保存(PUT /profile 的 babySex 字段)。 -->
|
||||
<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>
|
||||
<!-- 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>
|
||||
<!-- 预产期排在孕周前面:它是「源」,填了才解锁下面两格。
|
||||
默认值不写死在标记里 —— 由 JS 按「打开这一刻 + 3 个月」现算 -->
|
||||
<!-- 原生 date 空值时是一片空白(不支持 placeholder),叠一层文字占位:
|
||||
有值由 JS 隐藏(fillProfileForm / pgDue change 都会调 syncDuePh) -->
|
||||
<label class="fld fld-date"><span>预产期</span>
|
||||
<span class="due-box"><input id="pgDue" type="date"><span class="date-ph" id="pgDuePh">- 年 - 月 - 日</span></span>
|
||||
</label>
|
||||
<div class="fld-row">
|
||||
<!-- 孕周/天数由预产期自动算出,打开时按档案回填;新用户留空并锁定 -->
|
||||
<label class="fld"><span>当前孕周</span><input id="pgWeek" type="number" min="0" max="42" placeholder="—" disabled></label>
|
||||
<label class="fld"><span>额外天数</span><input id="pgDay" type="number" min="0" max="6" placeholder="—" disabled></label>
|
||||
</div>
|
||||
<p class="fld-hint" id="pgHint">填完预产期后自动算出,算得不准可以自己微调。</p>
|
||||
<div class="modal-foot">
|
||||
|
||||
<div class="modal-foot pf-foot">
|
||||
<button class="dl-btn ghost" data-modal="close">取消</button>
|
||||
<!-- ⚠️ 只剩这一个保存按钮。原 #pgSave 已随 modalPreg 一起删除,
|
||||
它那套逻辑(校验预产期、夹取孕周天数、写 state、调 syncPregnancy())
|
||||
整段并进了 js 里的 #pfSave 处理器。 -->
|
||||
<button class="dl-btn main" id="pfSave">保存</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
137
js/app.js
137
js/app.js
@@ -3,7 +3,7 @@
|
||||
(function () {
|
||||
'use strict';
|
||||
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
|
||||
var VER = '26100137';
|
||||
var VER = '26100138';
|
||||
const $ = (s, c) => (c || document).querySelector(s);
|
||||
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
|
||||
|
||||
@@ -890,14 +890,10 @@
|
||||
return d.getFullYear() + '-' + p(d.getMonth() + 1) + '-' + p(d.getDate());
|
||||
}
|
||||
|
||||
/* 2026-10-01 资料弹窗改版后:孕周两格不再锁(「按当前孕周」那页本来就是让人从零填的),
|
||||
这里只负责没预产期时把两格清空。函数名留着,调用点不用动。 */
|
||||
function lockWeekFields(locked) {
|
||||
pgWeek.disabled = pgDay.disabled = locked;
|
||||
/* 锁着的时候连数字也不留 —— 没填预产期就没有「当前孕周」这回事,
|
||||
留个旧值在灰格子里反而像是已经填好了 */
|
||||
if (locked) { pgWeek.value = ''; pgDay.value = ''; }
|
||||
pgHint.textContent = locked
|
||||
? '还没填预产期,孕周先锁着 —— 选好上面的预产期就会自动算出来。'
|
||||
: '已按预产期自动算出,觉得不准可以自己微调。';
|
||||
}
|
||||
/* 缓存戳重载:只换查询串(?t=…),两处都不能丢:
|
||||
① URL 已有 query 时必须用 & 拼接 —— 直接拼 '?t=' 会把第二个 ? 吃进已有参数值里
|
||||
@@ -944,6 +940,8 @@
|
||||
lockWeekFields(true);
|
||||
}
|
||||
syncDuePh();
|
||||
setPgMode('due');
|
||||
if (pgLmp) { pgLmp.value = ''; pgLmp.dispatchEvent(new Event('change')); }
|
||||
/* 头像预览跟随卡片(有正式头像才显示;新用户空圆 + +号) */
|
||||
const _me = $('#meAvatar');
|
||||
if (_me && _me.classList.contains('has-img') && meAvatarImg && meAvatarImg.src) {
|
||||
@@ -955,13 +953,72 @@
|
||||
}
|
||||
}
|
||||
|
||||
/* 预产期占位层:空值显示「- 年 - 月 - 日」,选了值隐藏 */
|
||||
/* 预产期按钮上的文字 + 顶部结果条,都从 #pgDue 现算(唯一数据源)。
|
||||
函数名沿用 syncDuePh(原来只管「- 年 - 月 - 日」占位层),调用点不用动。 */
|
||||
const WK = '日一二三四五六';
|
||||
function fmtDay(d) { return d.getFullYear() + '年' + (d.getMonth() + 1) + '月' + d.getDate() + '日 周' + WK[d.getDay()]; }
|
||||
function syncDuePh() {
|
||||
const ph = $('#pgDuePh');
|
||||
if (ph) ph.style.display = pgDue.value ? 'none' : 'block';
|
||||
pgDue.classList.toggle('due-empty', !pgDue.value);
|
||||
const due = parseDay(pgDue.value);
|
||||
const t = $('#pgDueTxt');
|
||||
if (t) { t.textContent = due ? fmtDay(due) : '点这里选预产期'; t.parentNode.classList.toggle('is-set', !!due); }
|
||||
const sum = $('#pgSum');
|
||||
if (!sum) return;
|
||||
sum.classList.toggle('is-set', !!due);
|
||||
if (!due) {
|
||||
$('#pgSumT').textContent = '还没填预产期';
|
||||
$('#pgSumS').textContent = '选一种方式填一下,马上就好';
|
||||
return;
|
||||
}
|
||||
const left = Math.round((midnight(due) - midnight(new Date())) / DAY_MS);
|
||||
if (left < 0) {
|
||||
$('#pgSumT').textContent = '宝宝已经出生啦';
|
||||
$('#pgSumS').textContent = '预产期 ' + fmtDay(due);
|
||||
return;
|
||||
}
|
||||
const ga = gaFromDue(due);
|
||||
$('#pgSumT').textContent = '孕 ' + Math.floor(ga / 7) + ' 周' + (ga % 7 ? ' +' + (ga % 7) + ' 天' : '');
|
||||
$('#pgSumS').textContent = (left ? '还有 ' + left + ' 天见面' : '就是今天啦') + ' · 预产期 ' + (due.getMonth() + 1) + '月' + due.getDate() + '日';
|
||||
}
|
||||
|
||||
/* 三种填法切换(只切面板;数据都落在 #pgDue) */
|
||||
function setPgMode(m) {
|
||||
$$('#pgMode .seg-b').forEach(b => b.classList.toggle('is-active', b.dataset.mode === m));
|
||||
$$('#modalProfile .pg-pane').forEach(p => p.classList.toggle('is-on', p.dataset.pane === m));
|
||||
}
|
||||
$$('#pgMode .seg-b').forEach(b => b.addEventListener('click', () => { setPgMode(b.dataset.mode); haptic(); }));
|
||||
|
||||
/* 孕周步进:空着时从 12 周起步(一般建档时间),天数 6 再 + 进位到下一周 */
|
||||
$$('#modalProfile .stp-b').forEach(b => b.addEventListener('click', () => {
|
||||
let w = parseInt(pgWeek.value, 10), d = parseInt(pgDay.value, 10);
|
||||
if (isNaN(w)) w = 12;
|
||||
if (isNaN(d)) d = 0;
|
||||
const k = +b.dataset.d;
|
||||
if (b.dataset.stp === 'w') w += k;
|
||||
else {
|
||||
d += k;
|
||||
if (d > 6) { d = 0; w += 1; }
|
||||
if (d < 0) { d = w > 0 ? 6 : 0; if (w > 0) w -= 1; }
|
||||
}
|
||||
pgWeek.value = Math.min(42, Math.max(0, w));
|
||||
pgDay.value = d;
|
||||
pgWeek.dispatchEvent(new Event('change'));
|
||||
haptic();
|
||||
}));
|
||||
|
||||
/* 末次月经第一天 + 280 天 = 预产期 */
|
||||
const pgLmp = $('#pgLmp');
|
||||
if (pgLmp) pgLmp.addEventListener('change', () => {
|
||||
const l = parseDay(pgLmp.value);
|
||||
const t = $('#pgLmpTxt');
|
||||
if (t) { t.textContent = l ? fmtDay(l) : '点这里选末次月经第一天'; t.parentNode.classList.toggle('is-set', !!l); }
|
||||
if (!l) return;
|
||||
pgDue.value = toISO(new Date(l.getFullYear(), l.getMonth(), l.getDate() + 280));
|
||||
pgDue.dispatchEvent(new Event('change'));
|
||||
});
|
||||
|
||||
/* 小名建议:点一下填进去 */
|
||||
$$('#pgNameChips .nm-c').forEach(b => b.addEventListener('click', () => { pgName.value = b.textContent; haptic(); }));
|
||||
|
||||
/* 预产期一变,孕周 / 额外天数自动算出来 */
|
||||
pgDue.addEventListener('change', () => {
|
||||
syncDuePh();
|
||||
@@ -979,6 +1036,7 @@
|
||||
const d = Math.min(6, Math.max(0, parseInt(pgDay.value, 10) || 0));
|
||||
pgWeek.value = w; pgDay.value = d;
|
||||
pgDue.value = toISO(dueFromGA(w * 7 + d));
|
||||
syncDuePh();
|
||||
}));
|
||||
|
||||
/* 性别三选一 */
|
||||
@@ -1373,7 +1431,8 @@
|
||||
$('#pfSave').addEventListener('click', () => {
|
||||
/* 预产期是一切的源头:孕周、剩余天数、信里的日期全从它算。没有它没什么可存的。 */
|
||||
if (!parseDay(pgDue.value)) {
|
||||
toast('先选一下预产期,孕周会自动算出来');
|
||||
toast('先填一下预产期,孕周会自动算出来');
|
||||
setPgMode('due');
|
||||
pgDue.focus();
|
||||
return;
|
||||
}
|
||||
@@ -4172,27 +4231,49 @@
|
||||
function setExchSub(t) {
|
||||
$$('#meExchSub, #homeExchSub').forEach(function (el) { el.textContent = t; });
|
||||
}
|
||||
function douImg(g) {
|
||||
return /^https?:/.test(g.img || '') ? g.img : EXCH_URL + String(g.img || '').replace(/^\//, '');
|
||||
}
|
||||
/* 一件商品的小卡(「我的」页 3 件 / 首页横滑 6 件共用),全用 DOM 拼,商品名不进 innerHTML */
|
||||
function douItem(g, cls, more) {
|
||||
const it = document.createElement('span'); it.className = cls;
|
||||
const pic = document.createElement('span'); pic.className = 'gi-pic';
|
||||
const im = document.createElement('img'); im.alt = ''; im.loading = 'lazy'; im.src = douImg(g);
|
||||
pic.appendChild(im);
|
||||
if (more && g.badge) { const bd = document.createElement('i'); bd.className = 'gi-badge'; bd.textContent = g.badge; pic.appendChild(bd); }
|
||||
if (more && g.stock <= 5) { const st = document.createElement('i'); st.className = 'gi-left'; st.textContent = '仅剩 ' + g.stock + ' 件'; pic.appendChild(st); }
|
||||
const nm = document.createElement('span'); nm.className = 'bd-nm'; nm.textContent = g.name || '';
|
||||
const pr = document.createElement('b'); pr.textContent = g.price + ' 兜豆';
|
||||
it.append(pic, nm, pr);
|
||||
return it;
|
||||
}
|
||||
function renderDrop(d) {
|
||||
const box = $('#meExchDrop'); if (!box) return;
|
||||
const now = d && d.server_time ? new Date(d.server_time).getTime() : Date.now();
|
||||
const goods = ((d && d.goods) || []).filter(function (g) {
|
||||
return g.stock > 0 && (!g.opens_at || new Date(g.opens_at).getTime() <= now);
|
||||
})
|
||||
.sort(function (x, y) { return (y.heat_30m || 0) - (x.heat_30m || 0); }).slice(0, 3);
|
||||
if (!goods.length) { box.classList.add('hide'); return; }
|
||||
const dr = d.drop || {};
|
||||
$('#meExchDropT').textContent = (dr.title || '本场好物') + (dr.open && dr.left != null ? ' · 剩 ' + dr.left + ' 件' : '');
|
||||
const wrap = $('#meExchGoods'); wrap.textContent = '';
|
||||
goods.forEach(function (g) {
|
||||
const it = document.createElement('span'); it.className = 'bd-it';
|
||||
const im = document.createElement('img'); im.alt = ''; im.loading = 'lazy';
|
||||
im.src = /^https?:/.test(g.img || '') ? g.img : EXCH_URL + String(g.img || '').replace(/^\//, '');
|
||||
const nm = document.createElement('span'); nm.className = 'bd-nm'; nm.textContent = g.name || '';
|
||||
const pr = document.createElement('b'); pr.textContent = g.price + ' 兜豆';
|
||||
it.append(im, nm, pr); wrap.appendChild(it);
|
||||
});
|
||||
box.classList.remove('hide');
|
||||
}).sort(function (x, y) { return (y.heat_30m || 0) - (x.heat_30m || 0); });
|
||||
/* 「我的」页:本场标题 + 热门前 3 */
|
||||
const box = $('#meExchDrop');
|
||||
if (box) {
|
||||
if (!goods.length) box.classList.add('hide');
|
||||
else {
|
||||
const dr = d.drop || {};
|
||||
$('#meExchDropT').textContent = (dr.title || '本场好物') + (dr.open && dr.left != null ? ' · 剩 ' + dr.left + ' 件' : '');
|
||||
const wrap = $('#meExchGoods'); wrap.textContent = '';
|
||||
goods.slice(0, 3).forEach(function (g) { wrap.appendChild(douItem(g, 'bd-it')); });
|
||||
box.classList.remove('hide');
|
||||
}
|
||||
}
|
||||
/* 首页:横滑前 6 件,带「热门」角标和「仅剩 N 件」 */
|
||||
const row = $('#homeExchGoods');
|
||||
if (row) {
|
||||
row.textContent = '';
|
||||
goods.slice(0, 6).forEach(function (g) { row.appendChild(douItem(g, 'ex-it', true)); });
|
||||
row.classList.toggle('hide', !goods.length);
|
||||
}
|
||||
}
|
||||
const _hxg = $('#homeExchGoods');
|
||||
if (_hxg) _hxg.addEventListener('click', function (e) { if (e.target.closest('.ex-it')) openExchange(); });
|
||||
function loadDou() {
|
||||
const oid = hashParam('openid'), uid = hashParam('unionid');
|
||||
const dropP = fetch(DOU_API + '/mall/drop').then(function (r) { return r.ok ? r.json() : null; }).catch(function () { return null; });
|
||||
|
||||
81
mama-box.css
81
mama-box.css
@@ -2995,3 +2995,84 @@ a.skip:focus{left:16px;top:16px}
|
||||
.es-main span{font-size:12px;font-weight:700;color:#B83F62;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
||||
.es-go{flex:0 0 auto;display:flex;align-items:center;padding:7px 12px;border-radius:99px;font-size:13px;font-weight:800;color:#fff;
|
||||
background:linear-gradient(135deg,#E8708F,#D9577A 60%,#B83F62);box-shadow:0 5px 12px rgba(184,63,98,.3)}
|
||||
|
||||
/* ================= 「我的资料」弹窗改版(2026-10-01)=================
|
||||
目标:好填。实时结果条在最上面;预产期三种填法;孕周用大号步进按钮,不用敲键盘;
|
||||
头像缩进「妈妈」卡和昵称并排;每组一张白卡,读起来是三件事,不是一长串输入框。 */
|
||||
.pf-modal{padding-bottom:0}
|
||||
.pf-lead{font-size:12.5px;color:var(--ink-2);margin-top:4px}
|
||||
.pf-sum{display:flex;align-items:center;gap:12px;margin-top:14px;padding:14px 16px;border-radius:20px;color:#fff;
|
||||
background:linear-gradient(135deg,#C9BEB6,#B2A69D);transition:background .3s}
|
||||
.pf-sum.is-set{background:radial-gradient(140px 90px at 100% 0%, rgba(255,214,150,.45), rgba(255,214,150,0) 70%),linear-gradient(135deg,#FF9A7A,#F2664A);
|
||||
box-shadow:0 10px 22px rgba(240,100,70,.28)}
|
||||
.ps-ico{width:40px;height:40px;border-radius:13px;background:rgba(255,255,255,.22);display:grid;place-items:center;flex:0 0 auto}
|
||||
.ps-main{display:flex;flex-direction:column;gap:2px;min-width:0}
|
||||
.ps-main b{font-size:19px;font-weight:900;letter-spacing:-.3px}
|
||||
.ps-main span{font-size:12.5px;opacity:.92}
|
||||
.pf-card{margin-top:12px;padding:14px 14px 16px;border-radius:20px;background:#fff;box-shadow:0 4px 14px rgba(160,110,90,.08)}
|
||||
.pf-ct{font-size:15px;font-weight:800;color:var(--ink);display:flex;align-items:center;gap:6px}
|
||||
.pf-ct em{font-style:normal;font-size:11px;font-weight:800;color:#fff;background:var(--brand);padding:1px 7px;border-radius:99px}
|
||||
.seg{display:flex;margin-top:12px;padding:4px;border-radius:14px;background:#F5EEE9}
|
||||
.seg-b{flex:1;padding:9px 2px;border-radius:11px;font-size:13px;font-weight:700;color:var(--ink-2);transition:all .2s}
|
||||
.seg-b.is-active{background:#fff;color:var(--brand);box-shadow:0 2px 8px rgba(160,90,60,.14)}
|
||||
.pg-pane{display:none;margin-top:12px}
|
||||
.pg-pane.is-on{display:block}
|
||||
.pg-tip{font-size:12px;color:var(--muted);margin-top:8px;line-height:1.5}
|
||||
.date-btn{position:relative;display:flex;align-items:center;gap:10px;padding:0 14px;min-height:54px;border-radius:16px;
|
||||
background:#FFF6F2;box-shadow:inset 0 0 0 1.5px #F6D6CA;cursor:pointer}
|
||||
.date-btn.is-set{background:#FDECE7;box-shadow:inset 0 0 0 1.8px var(--brand)}
|
||||
.db-ico{color:var(--brand);display:grid;place-items:center}
|
||||
.db-txt{flex:1;font-size:16px;font-weight:700;color:#B4A79C}
|
||||
.date-btn.is-set .db-txt{color:var(--ink)}
|
||||
/* 原生日期控件整块盖在按钮上、透明:点哪儿都能唤起系统日期选择器 */
|
||||
.date-btn input{position:absolute;inset:0;width:100%;height:100%;opacity:0;border:0;padding:0;margin:0;-webkit-appearance:none;appearance:none;cursor:pointer}
|
||||
.date-btn input::-webkit-calendar-picker-indicator{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}
|
||||
.stp-row{display:flex;gap:10px}
|
||||
.stp{flex:1;min-width:0}
|
||||
.stp-l{display:block;font-size:12.5px;font-weight:700;color:var(--ink-2);margin-bottom:7px}
|
||||
.stp-ctl{display:flex;align-items:center;height:54px;border-radius:16px;background:#FFF6F2;box-shadow:inset 0 0 0 1.5px #F6D6CA;overflow:hidden}
|
||||
.stp-b{flex:0 0 44px;height:100%;font-size:22px;font-weight:700;color:var(--brand)}
|
||||
.stp-b:active{background:#FBE2D9}
|
||||
.stp-ctl input{flex:1;min-width:0;width:100%;height:100%;border:0;background:none;text-align:center;font-size:22px;font-weight:900;color:var(--ink);
|
||||
-moz-appearance:textfield;padding:0}
|
||||
.stp-ctl input::-webkit-outer-spin-button,.stp-ctl input::-webkit-inner-spin-button{-webkit-appearance:none;margin:0}
|
||||
.stp-ctl input:focus{outline:none}
|
||||
.pf-fld{margin-top:12px}
|
||||
.nm-chips{display:flex;flex-wrap:wrap;gap:7px;margin-top:9px}
|
||||
.nm-c{padding:6px 12px;border-radius:99px;font-size:12.5px;font-weight:700;color:#B4573E;background:#FFF1EB}
|
||||
.nm-c:active{transform:scale(.95)}
|
||||
.pf-me{display:flex;align-items:flex-end;gap:14px}
|
||||
.pf-me .av-pick{padding:0;flex:0 0 auto}
|
||||
.pf-me .av-big{width:64px;height:64px}
|
||||
.pf-me .av-big>svg{width:36px;height:36px}
|
||||
.pf-me .fld{flex:1;margin-top:0}
|
||||
.pf-foot{position:sticky;bottom:0;margin:14px -22px 0;padding:12px 22px calc(14px + env(safe-area-inset-bottom));
|
||||
background:linear-gradient(rgba(250,245,241,0),#FAF5F1 30%)}
|
||||
.pf-foot .dl-btn.ghost{flex:0 0 96px}
|
||||
.pf-me .av-cam{width:24px;height:24px;right:-4px;bottom:-4px}
|
||||
.pf-me .av-cam svg{width:12px;height:12px}
|
||||
|
||||
/* 首页 · 兜豆兑好物模块:头部一条 + 横滑商品(2026-10-01 用户:「需要调出几个商品,不能直接是一条信息」) */
|
||||
.exch-mod{margin-top:12px;padding:6px 6px 12px;border-radius:24px;
|
||||
background:radial-gradient(160px 110px at 100% 0%, rgba(255,200,87,.32), rgba(255,200,87,0) 70%),linear-gradient(160deg,#FFF8F1,#FFEAF0 60%,#FFDCE6);
|
||||
box-shadow:0 10px 24px rgba(184,63,98,.14),inset 0 0 0 1px rgba(255,255,255,.75)}
|
||||
.exch-mod .exch-strip{margin-top:0;background:none;box-shadow:none;padding:8px 8px 6px}
|
||||
.ex-goods{display:flex;gap:9px;overflow-x:auto;padding:6px 8px 2px;scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;scrollbar-width:none}
|
||||
.ex-goods::-webkit-scrollbar{display:none}
|
||||
.ex-goods.hide{display:none}
|
||||
.ex-it{flex:0 0 96px;scroll-snap-align:start;display:flex;flex-direction:column;gap:3px;padding:6px 6px 8px;border-radius:16px;background:#fff;
|
||||
box-shadow:0 4px 10px rgba(184,63,98,.08);cursor:pointer}
|
||||
.ex-it:active{transform:scale(.97)}
|
||||
.gi-pic{position:relative;display:block}
|
||||
.gi-pic img{display:block;width:100%;aspect-ratio:1;object-fit:cover;border-radius:12px;background:#FFF1F4}
|
||||
.gi-badge{position:absolute;left:4px;top:4px;font-style:normal;font-size:10px;font-weight:800;color:#fff;background:#E8344E;padding:1px 6px;border-radius:99px}
|
||||
.gi-left{position:absolute;left:4px;right:4px;bottom:4px;text-align:center;font-style:normal;font-size:10px;font-weight:800;color:#fff;
|
||||
background:rgba(74,53,48,.62);padding:1px 0;border-radius:8px}
|
||||
.ex-it .bd-nm{font-size:12px;color:#4A3530;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}
|
||||
.ex-it b{font-size:12px;font-weight:900;color:#D9577A}
|
||||
|
||||
/* 「我的」页:孕期卡和兜豆卡对调 —— 孕期卡半压在头部底边,阴影加深一点才「浮」得起来 */
|
||||
#screen-me .page-body.no-top>.preg-card:first-of-type{box-shadow:0 14px 30px rgba(140,56,34,.18),0 2px 6px rgba(140,56,34,.08)}
|
||||
.seg-b{text-align:center;white-space:nowrap}
|
||||
.stp-b{width:48px;min-width:48px;display:grid;place-items:center;padding:0}
|
||||
.stp-ctl input{width:0}
|
||||
|
||||
Reference in New Issue
Block a user